diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..4f2e5a98
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+# https://github.community/t/how-to-change-the-category/2261/3
+videojs-*.js linguist-detectable=false
+video.min.js linguist-detectable=false
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 00000000..079d85eb
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,15 @@
+# Default and lowest precedence. If none of the below matches, @iv-org/developers would be requested for review.
+* @iv-org/developers
+
+docker-compose.yml @unixfox
+docker/ @unixfox
+kubernetes/ @unixfox
+
+README.md @thefrenchghosty
+config/config.example.yml @thefrenchghosty @SamantazFox @unixfox
+
+scripts/ @syeopite
+shards.lock @syeopite
+shards.yml @syeopite
+
+src/invidious/helpers/youtube_api.cr @SamantazFox
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 66aacff9..3bb4c491 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,41 +1,72 @@
name: Invidious CI
on:
+ schedule:
+ - cron: "0 0 * * *" # Every day at 00:00
push:
branches:
- "master"
- "api-only"
pull_request:
branches: "*"
+ paths-ignore:
+ - "*.md"
+ - LICENCE
+ - TRANSLATION
+ - invidious.service
+ - .git*
+ - .editorconfig
+
+ - screenshots/*
+ - assets/**
+ - locales/*
+ - config/**
+ - .github/ISSUE_TEMPLATE/*
+ - kubernetes/**
jobs:
build:
-
+
runs-on: ubuntu-latest
-
+
+ name: "build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }}"
+
+ continue-on-error: ${{ !matrix.stable }}
+
+ strategy:
+ fail-fast: false
+ matrix:
+ stable: [true]
+ crystal:
+ - 1.0.0
+ - 1.1.1
+ include:
+ - crystal: nightly
+ stable: false
+
steps:
- uses: actions/checkout@v2
-
+
- name: Install Crystal
uses: oprypin/install-crystal@v1.2.4
with:
- crystal: 0.36.1
-
+ crystal: ${{ matrix.crystal }}
+
- name: Cache Shards
uses: actions/cache@v2
with:
path: ./lib
key: shards-${{ hashFiles('shard.lock') }}
-
+
- name: Install Shards
run: |
if ! shards check; then
shards install
fi
-
+
- name: Run tests
run: crystal spec
-
+
- name: Run lint
run: |
if ! crystal tool format --check; then
@@ -43,20 +74,50 @@ jobs:
git diff
exit 1
fi
-
+
- name: Build
run: crystal build --warnings all --error-on-warnings --error-trace src/invidious.cr
-
+
build-docker:
-
+
runs-on: ubuntu-latest
-
+
steps:
- uses: actions/checkout@v2
-
+
- name: Build Docker
+ run: docker-compose build --build-arg release=0
+
+ - name: Run Docker
run: docker-compose up -d
-
+
- name: Test Docker
run: while curl -Isf http://localhost:3000; do sleep 1; done
+ build-docker-arm64:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v1
+ with:
+ platforms: arm64
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v1
+
+ - name: Build Docker ARM64 image
+ uses: docker/build-push-action@v2
+ with:
+ context: .
+ file: docker/Dockerfile.arm64
+ platforms: linux/arm64/v8
+ build-args: release=0
+
+ - name: Test Docker
+ run: while curl -Isf http://localhost:3000; do sleep 1; done
+
+
diff --git a/.github/workflows/container-release.yml b/.github/workflows/container-release.yml
index 1f811b7c..77b92c6f 100644
--- a/.github/workflows/container-release.yml
+++ b/.github/workflows/container-release.yml
@@ -4,9 +4,18 @@ on:
push:
branches:
- "master"
- schedule:
- - cron: 0 0 * * *
+ paths-ignore:
+ - "*.md"
+ - LICENCE
+ - TRANSLATION
+ - invidious.service
+ - .git*
+ - .editorconfig
+ - screenshots/*
+ - .github/ISSUE_TEMPLATE/*
+ - kubernetes/**
+
jobs:
release:
runs-on: ubuntu-latest
@@ -14,9 +23,24 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
+
+ - name: Install Crystal
+ uses: oprypin/install-crystal@v1.2.4
+ with:
+ crystal: 1.1.1
+
+ - name: Run lint
+ run: |
+ if ! crystal tool format --check; then
+ crystal tool format
+ git diff
+ exit 1
+ fi
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
+ with:
+ platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
@@ -28,12 +52,26 @@ jobs:
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
- - name: Build and push for Push Event
+ - name: Build and push Docker AMD64 image for Push Event
if: github.ref == 'refs/heads/master'
uses: docker/build-push-action@v2
with:
context: .
file: docker/Dockerfile
+ platforms: linux/amd64
labels: quay.expires-after=12w
push: true
tags: quay.io/invidious/invidious:${{ github.sha }},quay.io/invidious/invidious:latest
+ build-args: release=1
+
+ - name: Build and push Docker ARM64 image for Push Event
+ if: github.ref == 'refs/heads/master'
+ uses: docker/build-push-action@v2
+ with:
+ context: .
+ file: docker/Dockerfile.arm64
+ platforms: linux/arm64/v8
+ labels: quay.expires-after=12w
+ push: true
+ tags: quay.io/invidious/invidious:${{ github.sha }}-arm64,quay.io/invidious/invidious:latest-arm64
+ build-args: release=1
diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml
new file mode 100644
index 00000000..aa9e2b31
--- /dev/null
+++ b/.github/workflows/lock.yml
@@ -0,0 +1,22 @@
+# Documentation: https://github.com/marketplace/actions/lock-threads
+
+name: 'Lock Threads'
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: "0 */12 * * *"
+
+jobs:
+ lock:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: dessant/lock-threads@v2
+ with:
+ github-token: ${{ github.token }}
+ issue-lock-inactive-days: '240'
+ pr-lock-inactive-days: '240'
+ issue-lock-reason: 'resolved'
+ pr-lock-reason: 'resolved'
+
+ # issue-lock-comment: 'This issue has been automatically locked since there has not been any activity in it in the last 30 days. If this is still applicable to the current version of Invidious feel free to open a new issue.'
+ # pr-lock-comment: 'This pull request has been automatically locked since there has not been any activity in it in the last 30 days. If you want to tell us about needed or wanted changes or if problems related to this code are discovered, feel free to open an issue or a new pull request.'
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index e452274b..86275da7 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -14,9 +14,11 @@ jobs:
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 365
+ days-before-pr-stale: 45 # PRs should be active. Anything that hasn't had activity in more than 45 days should be considered abandoned.
days-before-close: 30
+ exempt-pr-labels: blocked
stale-issue-message: 'This issue has been automatically marked as stale and will be closed in 30 days because it has not had recent activity and is much likely outdated. If you think this issue is still relevant and applicable, you just have to post a comment and it will be unmarked.'
- stale-pr-message: 'This pull request has been automatically marked as stale and will be closed in 30 days because it has not had recent activity and is much likely outdated. If you think this pull request is still relevant and applicable, you just have to post a comment and it will be unmarked.'
+ stale-pr-message: 'This pull request has been automatically marked as stale and will be closed in 30 days because it has not had recent activity and is much likely abandoned or outdated. If you think this pull request is still relevant and applicable, you just have to post a comment and it will be unmarked.'
stale-issue-label: "stale"
stale-pr-label: "stale"
ascending: true
diff --git a/README.md b/README.md
index ba6c1be5..3f7fa8a7 100644
--- a/README.md
+++ b/README.md
@@ -1,73 +1,133 @@
-
Invidious
+
-Public Invidious instances are listed on the documentation website: https://instances.invidious.io/
----
+## Screenshots
-## Invidious features:
+| Player | Preferences | Subscriptions |
+|-------------------------------------|-------------------------------------|---------------------------------------|
+|  |  |  |
+|  |  |  |
-- [Copylefted libre software](https://github.com/iv-org/invidious) (AGPLv3+ licensed)
-- Lightweight (the homepage is ~4 KB compressed)
+
+## Features
+
+**User features**
+- Lightweight
- No ads
- No tracking
-- Javascript is 100% optional
-- Tools for managing subscriptions:
- - Only show unseen videos
- - Only show latest (or latest unseen) video from each channel
- - Delivers notifications from all subscribed channels
- - Automatically redirect homepage to feed
- - Import subscriptions from YouTube
-- Audio-only mode (and no need to keep window open on mobile)
-- Dark mode
-- Embed support
-- Set default player options (speed, quality, autoplay, loop)
-- Support for Reddit comments in place of YouTube comments
-- Import/Export subscriptions, watch history, preferences
+- No JavaScript required
+- Light/Dark themes
+- Customizable homepage
+- Subscriptions independant from Google
+- Notifications for all subscribed channels
+- Audio-only mode (with background play on mobile)
+- Support for Reddit comments
+- [Available in many languages](locales/), thanks to [our translators](#contribute)
+
+**Data import/export**
+- Import subscriptions from YouTube, NewPipe and Freetube
+- Import watch history from NewPipe
+- Export subscriptions to NewPipe and Freetube
+- Import/Export Invidious user data
+
+**Technical features**
+- Embedded video support
- [Developer API](https://docs.invidious.io/API.md)
-- Does not use any of the official YouTube APIs
-- No need to create a Google account to save subscriptions
-- No Code of Conduct
-- No Contributor license Agreement
-- Available in many languages, thanks to [Weblate](https://hosted.weblate.org/projects/invidious/)
+- Does not use official YouTube APIs
+- No Contributor License Agreement (CLA)
----
-## Screenshots:
+## Quick start
-| Player | Preferences | Subscriptions |
-| ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
-| [ ](screenshots/01_player.png?raw=true) | [ ](screenshots/02_preferences.png?raw=true) | [ ](screenshots/03_subscriptions.png?raw=true) |
-| [ ](screenshots/04_description.png?raw=true) | [ ](screenshots/05_preferences.png?raw=true) | [ ](screenshots/06_subscriptions.png?raw=true) |
+**Using invidious:**
----
+- [Select a public instance from the list](https://instances.invidious.io) and start watching videos right now!
-## Donate:
+**Hosting invidious:**
-Bitcoin (BTC): [bc1qfhe7rq3lqzuayzjxzyt9waz9ytrs09kla3tsgr](bitcoin:bc1qfhe7rq3lqzuayzjxzyt9waz9ytrs09kla3tsgr)
+- [Follow the installation instructions](https://docs.invidious.io/Installation.md)
-Monero (XMR): [41nMCtek197boJtiUvGnTFYMatrLEpnpkQDmUECqx5Es2uX3sTKKWVhSL76suXsG3LXqkEJBrCZBgPTwJrDp1FrZJfycGPR](monero:41nMCtek197boJtiUvGnTFYMatrLEpnpkQDmUECqx5Es2uX3sTKKWVhSL76suXsG3LXqkEJBrCZBgPTwJrDp1FrZJfycGPR)
----
+## Documentation
-## Documentation:
+The full documentation can be accessed online at https://docs.invidious.io/
-The complete documentation is available on https://docs.invidious.io/ (or alternatively on its own [Github repository](https://github.com/iv-org/documentation)).
+The documentation's source code is available in this repository:
+https://github.com/iv-org/documentation
----
+### Extensions
-## Extensions:
+We highly recommend the use of [Privacy Redirect](https://github.com/SimonBrazell/privacy-redirect#get),
+a browser extension that automatically redirects Youtube URLs to any Invidious instance and replaces
+embedded youtube videos on other websites with invidious.
-[Extensions](https://docs.invidious.io/Extensions.md) can be found in the wiki, as well as documentation for integrating it into other projects.
+The documentation contains a list of browser extensions that we recommended to use along with Invidious.
----
+You can read more here: https://docs.invidious.io/Extensions.md
-## Made with Invidious:
+
+## Contribute
+
+### Code
+
+1. Fork it ( https://github.com/iv-org/invidious/fork ).
+1. Create your feature branch (`git checkout -b my-new-feature`).
+1. Stage your files (`git add .`).
+1. Commit your changes (`git commit -am 'Add some feature'`).
+1. Push to the branch (`git push origin my-new-feature`).
+1. Create a new pull request ( https://github.com/iv-org/invidious/compare ).
+
+### Translations
+
+We use [Weblate](https://weblate.org) to manage Invidious translations.
+
+You can suggest new translations and/or correction here: https://hosted.weblate.org/engage/invidious/.
+
+Creating an account is not required, but recommended, especially if you want to contribute regularly.
+Weblate also allows you to log-in with major SSO providers like Github, Gitlab, BitBucket, Google, ...
+
+
+## Projects using Invidious
- [FreeTube](https://github.com/FreeTubeApp/FreeTube): A libre software YouTube app for privacy.
- [CloudTube](https://sr.ht/~cadence/tube/): A JavaScript-rich alternate YouTube player.
@@ -75,33 +135,25 @@ The complete documentation is available on https://docs.invidious.io/ (or altern
- [MusicPiped](https://github.com/deep-gaurav/MusicPiped): A material design music player that streams music from YouTube.
- [HoloPlay](https://github.com/stephane-r/HoloPlay): Funny Android application connecting on Invidious API's with search, playlists and favoris.
----
-## Contributing:
+## Donate
-[](https://github.com/iv-org/invidious/actions) [](https://hosted.weblate.org/engage/invidious/)
+Bitcoin (BTC): [bc1qfhe7rq3lqzuayzjxzyt9waz9ytrs09kla3tsgr](bitcoin:bc1qfhe7rq3lqzuayzjxzyt9waz9ytrs09kla3tsgr)
-1. Fork it ( https://github.com/iv-org/invidious/fork ).
-2. Create your feature branch (git checkout -b my-new-feature).
-3. Commit your changes (git commit -am 'Add some feature').
-4. Push to the branch (git push origin my-new-feature).
-5. Create a new pull request.
+Monero (XMR): [41nMCtek197boJtiUvGnTFYMatrLEpnpkQDmUECqx5Es2uX3sTKKWVhSL76suXsG3LXqkEJBrCZBgPTwJrDp1FrZJfycGPR](monero:41nMCtek197boJtiUvGnTFYMatrLEpnpkQDmUECqx5Es2uX3sTKKWVhSL76suXsG3LXqkEJBrCZBgPTwJrDp1FrZJfycGPR)
-### Translation:
+Ethereum (ETH): [0xD1F7E3Bfb19Ee5a52baED396Ad34717aF18d995B](ethereum:0xD1F7E3Bfb19Ee5a52baED396Ad34717aF18d995B)
-- Log in with an account you have elsewhere, or register an account and start translating at [Hosted Weblate](https://hosted.weblate.org/engage/invidious/).
+Litecoin (LTC): [ltc1q8787aq2xrseq5yx52axx8c4fqks88zj5vr0zx9](litecoin:ltc1q8787aq2xrseq5yx52axx8c4fqks88zj5vr0zx9)
----
-## Contact:
+## Liability
-Feel free to join our [Matrix room](https://matrix.to/#/#invidious:matrix.org), or #invidious on freenode. Both platforms are bridged together.
-
----
-
-## Liability:
-
-We take no responsibility for the use of our tool, or external instances provided by third parties. We strongly recommend you abide by the valid official regulations in your country. Furthermore, we refuse liability for any inappropriate use of Invidious, such as illegal downloading. This tool is provided to you in the spirit of free, open software.
+We take no responsibility for the use of our tool, or external instances
+provided by third parties. We strongly recommend you abide by the valid
+official regulations in your country. Furthermore, we refuse liability
+for any inappropriate use of Invidious, such as illegal downloading.
+This tool is provided to you in the spirit of free, open software.
You may view the LICENSE in which this software is provided to you [here](./LICENSE).
diff --git a/assets/css/default.css b/assets/css/default.css
index a76ecd48..ce6c30c9 100644
--- a/assets/css/default.css
+++ b/assets/css/default.css
@@ -5,6 +5,12 @@ body {
Arial, sans-serif;
}
+#contents {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+}
+
.deleted {
background-color: rgb(255, 0, 0, 0.5);
}
@@ -172,7 +178,7 @@ img.thumbnail {
flex: 1;
}
-.navbar > .searchbar {
+.searchbar {
flex-grow: 2; /* take double the space of the other items */
}
@@ -185,7 +191,7 @@ img.thumbnail {
display: inline;
}
-.navbar > .searchbar .pure-form input[type="search"] {
+.searchbar .pure-form input[type="search"] {
margin-bottom: 1px;
border-top: 0;
@@ -210,12 +216,12 @@ input[type="search"]::-webkit-search-cancel-button {
background-size: 14px;
}
-.navbar > .searchbar .pure-form fieldset {
+.searchbar .pure-form fieldset {
padding: 0;
}
/* attract focus to the searchbar by adding a subtle transition */
-.navbar > .searchbar .pure-form input[type="search"]:focus {
+.searchbar .pure-form input[type="search"]:focus {
margin-bottom: 0px;
border-bottom: 2px solid #aaa;
}
@@ -276,18 +282,35 @@ input[type="search"]::-webkit-search-cancel-button {
}
}
+
+/*
+ * Video "cards" (results/playlist/channel videos)
+ */
+
+.video-card-row { margin: 15px 0; }
+
+.flexible { display: flex; }
+.flex-left { flex: 1 1 100%; flex-wrap: wrap; }
+.flex-right { flex: 1 0 max-content; flex-wrap: nowrap; }
+
+p.channel-name { margin: 0; }
+p.video-data { margin: 0; font-weight: bold; font-size: 80%; }
+
+
/*
* Footer
*/
-.footer {
- color: #666666;
- margin: 2em 0;
+footer {
+ color: #919191;
+ margin-top: auto;
+ padding: 1.5em 0;
text-align: center;
+ max-height: 30vh;
}
-body .footer a {
- color: inherit;
+footer a {
+ color: #919191 !important;
text-decoration: underline;
}
@@ -302,194 +325,15 @@ body .footer a {
}
}
-/* Control Bar */
-@media screen and (max-width: 640px) {
- .video-js .vjs-control-bar,
- .vjs-menu-button-popup .vjs-menu .vjs-menu-content {
- overflow-x: scroll;
- }
-}
-
-ul.vjs-menu-content::-webkit-scrollbar {
- display: none;
-}
-
-.vjs-user-inactive {
- cursor: none;
-}
-
-.video-js .vjs-text-track-display > div > div > div {
- background-color: rgba(0, 0, 0, 0.75) !important;
- border-radius: 9px !important;
- padding: 5px !important;
-}
-
-.vjs-play-control,
-.vjs-volume-panel,
-.vjs-current-time,
-.vjs-time-control,
-.vjs-duration,
-.vjs-progress-control,
-.vjs-remaining-time {
- order: 1;
-}
-
-.vjs-captions-button {
- order: 2;
-}
-
-.vjs-quality-selector,
-.video-js .vjs-http-source-selector {
- order: 3;
-}
-
-.vjs-playback-rate {
- order: 4;
-}
-
-.vjs-share-control {
- order: 5;
-}
-
-.vjs-fullscreen-control {
- order: 6;
-}
-
-.vjs-playback-rate > .vjs-menu {
- width: 50px;
-}
-
-.vjs-control-bar {
- display: flex;
- flex-direction: row;
- scrollbar-width: none;
-}
-
-.vjs-control-bar::-webkit-scrollbar {
- display: none;
-}
-
-.video-js .vjs-icon-cog {
- font-size: 18px;
-}
-
-.video-js .vjs-control-bar,
-.vjs-menu-button-popup .vjs-menu .vjs-menu-content {
- background-color: rgba(35, 35, 35, 0.75);
-}
-
-.vjs-menu li.vjs-menu-item:focus,
-.vjs-menu li.vjs-menu-item:hover {
- background-color: rgba(255, 255, 255, 0.75);
- color: rgba(49, 49, 51, 0.75);
-}
-
-.vjs-menu li.vjs-selected,
-.vjs-menu li.vjs-selected:focus,
-.vjs-menu li.vjs-selected:hover {
- background-color: rgba(0, 182, 240, 0.75);
-}
-
-/* Progress Bar */
-.video-js .vjs-slider {
- background-color: rgba(15, 15, 15, 0.5);
-}
-
fieldset > select,
span > select {
color: rgba(49, 49, 51, 1);
}
-.video-js .vjs-load-progress,
-.video-js .vjs-load-progress div {
- background: rgba(87, 87, 88, 1);
-}
-
-.video-js .vjs-slider:hover,
-.video-js button:hover {
- color: rgba(0, 182, 240, 1);
-}
-
-.video-js .vjs-play-progress {
- background-color: rgba(0, 182, 240, 1);
-}
-
-/* Overlay */
-.video-js .vjs-overlay {
- background-color: rgba(35, 35, 35, 0.75);
- color: rgba(255, 255, 255, 1);
-}
-
-/* ProgressBar marker */
-.vjs-marker {
- background-color: rgba(255, 255, 255, 1);
- z-index: 0;
-}
-
-/* Big "Play" Button */
-.video-js .vjs-big-play-button {
- background-color: rgba(35, 35, 35, 0.5);
-}
-
-.video-js:hover .vjs-big-play-button {
- background-color: rgba(35, 35, 35, 0.75);
-}
-
-.video-js .vjs-current-time,
-.video-js .vjs-time-divider,
-.video-js .vjs-duration {
- display: block;
-}
-
-.video-js .vjs-time-divider {
- min-width: 0px;
- padding-left: 0px;
- padding-right: 0px;
-}
-
-.video-js .vjs-poster {
- background-size: cover;
- object-fit: cover;
-}
-
-.player-dimensions.vjs-fluid {
- padding-top: 82vh;
-}
-
-video.video-js {
- position: absolute;
- height: 100%;
-}
-
-#player-container {
- position: relative;
- padding-bottom: 82vh;
- height: 0;
-}
-
.pure-control-group label {
word-wrap: normal;
}
-.video-js.player-style-invidious {
- /* This is already the default */
-}
-
-.video-js.player-style-youtube .vjs-control-bar {
- display: flex;
- flex-direction: row;
-}
-.video-js.player-style-youtube .vjs-big-play-button {
- /*
- Styles copied from video-js.min.css, definition of
- .vjs-big-play-centered .vjs-big-play-button
- */
- top: 50%;
- left: 50%;
- margin-top: -0.81666em;
- margin-left: -1.5em;
-}
-
/*
* Light theme
*/
@@ -586,7 +430,7 @@ body.dark-theme {
color: #f0f0f0;
}
-.dark-theme .navbar > .searchbar input {
+.dark-theme .searchbar input {
background-color: inherit;
color: inherit;
}
@@ -625,7 +469,7 @@ body.dark-theme {
color: #f0f0f0;
}
- .no-theme .navbar > .searchbar input {
+ .no-theme .searchbar input {
background-color: inherit;
color: inherit;
}
@@ -641,7 +485,7 @@ body.dark-theme {
}
#filters > summary {
- display: inline-block;
+ display: block;
margin-bottom: 15px;
}
@@ -654,3 +498,50 @@ body.dark-theme {
content: "[ - ]";
font-size: 1.5em;
}
+
+/*With commit d9528f5 all contents of the page is now within a flexbox. However,
+the hr element is rendered improperly within one.
+See https://stackoverflow.com/a/34372979 for more info */
+hr {
+ margin: 10px 0 10px 0;
+}
+
+/* Description Expansion Styling*/
+#descexpansionbutton {
+ display: none
+}
+
+#descexpansionbutton ~ div {
+ overflow: hidden;
+ height: 8.3em;
+}
+
+#descexpansionbutton:checked ~ div {
+ overflow: unset;
+ height: 100%;
+}
+
+#descexpansionbutton ~ label {
+ order: 1;
+ margin-top: 20px;
+}
+
+/* Bidi (bidirectional text) support */
+h1,
+h2,
+h3,
+h4,
+h5,
+p,
+#descriptionWrapper,
+#description-box {
+ unicode-bidi: plaintext;
+ text-align: start;
+}
+
+#descriptionWrapper {
+ max-width: 600px;
+}
+
+/* Center the "invidious" logo on the search page */
+#logo > h1 { text-align: center; }
diff --git a/assets/css/embed.css b/assets/css/embed.css
index 12fefe58..466a284a 100644
--- a/assets/css/embed.css
+++ b/assets/css/embed.css
@@ -8,3 +8,19 @@
height: auto;
z-index: -100;
}
+
+.watch-on-invidious {
+ font-size: 1.3em !important;
+ font-weight: bold;
+ white-space: nowrap;
+ margin: 0 1em 0 1em !important;
+ order: 3;
+}
+
+.watch-on-invidious > a {
+ color: white;
+}
+
+.watch-on-invidious > a:hover {
+ color: rgba(0, 182, 240, 1);;
+}
diff --git a/assets/css/empty.css b/assets/css/empty.css
new file mode 100644
index 00000000..6ad1515d
--- /dev/null
+++ b/assets/css/empty.css
@@ -0,0 +1,16 @@
+#search-widget {
+ text-align: center;
+ margin: 20vh 0 50px 0;
+}
+
+#logo > h1 {
+ font-size: 3.5em;
+ margin: 0;
+ padding: 0;
+}
+
+@media screen and (max-width: 1500px) and (max-height: 1000px) {
+ #logo > h1 {
+ font-size: 10vmin;
+ }
+}
diff --git a/assets/css/player.css b/assets/css/player.css
new file mode 100644
index 00000000..656fb48c
--- /dev/null
+++ b/assets/css/player.css
@@ -0,0 +1,250 @@
+/* Youtube player style */
+.video-js.player-style-youtube .vjs-progress-control {
+ height: 0;
+}
+
+.video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, .video-js.player-style-youtube .vjs-progress-control {
+ position: absolute;
+ right: 0;
+ left: 0;
+ width: 100%;
+ margin: 0;
+}
+
+.video-js.player-style-youtube .vjs-control-bar {
+ background: linear-gradient(rgba(0,0,0,0.1), rgba(0, 0, 0,0.5));
+}
+
+.video-js.player-style-youtube .vjs-slider {
+ background-color: rgba(255,255,255,0.2);
+}
+
+.video-js.player-style-youtube .vjs-load-progress > div {
+ background-color: rgba(255,255,255,0.5);
+}
+
+.video-js.player-style-youtube .vjs-play-progress {
+ background-color: red;
+}
+
+.video-js.player-style-youtube .vjs-progress-control:hover .vjs-progress-holder {
+ font-size: 15px;
+}
+
+.video-js.player-style-youtube .vjs-control-bar > .vjs-spacer {
+ flex: 1;
+ order: 2;
+}
+
+.video-js.player-style-youtube .vjs-play-progress .vjs-time-tooltip {
+ display: none;
+}
+
+.video-js.player-style-youtube .vjs-play-progress::before {
+ color: red;
+ font-size: 0.85em;
+ display: none;
+}
+
+.video-js.player-style-youtube .vjs-progress-holder:hover .vjs-play-progress::before {
+ display: unset;
+}
+
+.video-js.player-style-youtube .vjs-control-bar {
+ display: flex;
+ flex-direction: row;
+}
+
+.video-js.player-style-youtube .vjs-big-play-button {
+ /*
+ Styles copied from video-js.min.css, definition of
+ .vjs-big-play-centered .vjs-big-play-button
+ */
+ top: 50%;
+ left: 50%;
+ margin-top: -0.81666em;
+ margin-left: -1.5em;
+}
+
+.video-js.player-style-youtube .vjs-menu-button-popup .vjs-menu {
+ margin-bottom: 2em;
+}
+
+ul.vjs-menu-content::-webkit-scrollbar {
+ display: none;
+}
+
+.vjs-user-inactive {
+ cursor: none;
+}
+
+.video-js .vjs-text-track-display > div > div > div {
+ background-color: rgba(0, 0, 0, 0.75) !important;
+ border-radius: 9px !important;
+ padding: 5px !important;
+}
+
+.vjs-play-control,
+.vjs-volume-panel,
+.vjs-current-time,
+.vjs-time-control,
+.vjs-duration,
+.vjs-progress-control,
+.vjs-remaining-time {
+ order: 1;
+}
+
+.vjs-captions-button {
+ order: 2;
+}
+
+.vjs-quality-selector,
+.video-js .vjs-http-source-selector {
+ order: 3;
+}
+
+.vjs-playback-rate {
+ order: 4;
+}
+
+.vjs-share-control {
+ order: 5;
+}
+
+.vjs-fullscreen-control {
+ order: 6;
+}
+
+.vjs-playback-rate > .vjs-menu {
+ width: 50px;
+}
+
+.vjs-control-bar {
+ display: flex;
+ flex-direction: row;
+ scrollbar-width: none;
+}
+
+.vjs-control-bar::-webkit-scrollbar {
+ display: none;
+}
+
+.video-js .vjs-icon-cog {
+ font-size: 18px;
+}
+
+.video-js .vjs-control-bar,
+.vjs-menu-button-popup .vjs-menu .vjs-menu-content {
+ background-color: rgba(35, 35, 35, 0.75);
+}
+
+.vjs-menu li.vjs-menu-item:focus,
+.vjs-menu li.vjs-menu-item:hover {
+ background-color: rgba(255, 255, 255, 0.75);
+ color: rgba(49, 49, 51, 0.75);
+}
+
+.vjs-menu li.vjs-selected,
+.vjs-menu li.vjs-selected:focus,
+.vjs-menu li.vjs-selected:hover {
+ background-color: rgba(0, 182, 240, 0.75);
+}
+
+/* Progress Bar */
+.video-js .vjs-slider {
+ background-color: rgba(15, 15, 15, 0.5);
+}
+
+.video-js .vjs-load-progress,
+.video-js .vjs-load-progress div {
+ background: rgba(87, 87, 88, 1);
+}
+
+.video-js .vjs-slider:hover,
+.video-js button:hover {
+ color: rgba(0, 182, 240, 1);
+}
+
+.video-js.player-style-invidious .vjs-play-progress {
+ background-color: rgba(0, 182, 240, 1);
+}
+vjs-menu-content
+/* Overlay */
+.video-js .vjs-overlay {
+ background-color: rgba(35, 35, 35, 0.75);
+ color: rgba(255, 255, 255, 1);
+}
+
+/* ProgressBar marker */
+.vjs-marker {
+ background-color: rgba(255, 255, 255, 1);
+ z-index: 0;
+}
+
+/* Big "Play" Button */
+.video-js .vjs-big-play-button {
+ background-color: rgba(35, 35, 35, 0.5);
+}
+
+.video-js:hover .vjs-big-play-button {
+ background-color: rgba(35, 35, 35, 0.75);
+}
+
+.video-js .vjs-current-time,
+.video-js .vjs-time-divider,
+.video-js .vjs-duration {
+ display: block;
+}
+
+.video-js .vjs-time-divider {
+ min-width: 0px;
+ padding-left: 0px;
+ padding-right: 0px;
+}
+
+.video-js .vjs-poster {
+ background-size: cover;
+ object-fit: cover;
+}
+
+.player-dimensions.vjs-fluid {
+ padding-top: 82vh;
+}
+
+video.video-js {
+ position: absolute;
+ height: 100%;
+}
+
+#player-container {
+ position: relative;
+ padding-bottom: 82vh;
+ height: 0;
+}
+
+.mobile-operations-bar {
+ display: flex;
+ position: absolute;
+ top: 0;
+ right: 1px !important;
+ left: initial !important;
+ width: initial !important;
+}
+
+.mobile-operations-bar ul {
+ position: absolute !important;
+ bottom: unset !important;
+ top: 1.5em;
+}
+
+@media screen and (max-width: 700px) {
+ .video-js .vjs-share {
+ justify-content: unset;
+ }
+}
+
+@media screen and (max-width: 650px) {
+ .vjs-modal-dialog-content {
+ overflow-x: hidden;
+ }
+}
diff --git a/assets/css/videojs-mobile-ui.css b/assets/css/videojs-mobile-ui.css
new file mode 100644
index 00000000..c307274a
--- /dev/null
+++ b/assets/css/videojs-mobile-ui.css
@@ -0,0 +1,7 @@
+/**
+ * videojs-mobile-ui
+ * @version 0.5.2
+ * @copyright 2021 mister-ben
+ * @license MIT
+ */
+@keyframes fadeAndScale{0%{opacity:0}25%{opacity:1}100%{opacity:0}}.video-js.vjs-has-started .vjs-touch-overlay{position:absolute;pointer-events:auto;top:0}.video-js .vjs-touch-overlay{display:block;width:100%;height:100%;pointer-events:none}.video-js .vjs-touch-overlay.skip{opacity:0;animation:fadeAndScale 0.6s linear;background-repeat:no-repeat;background-position:80% center;background-size:10%;background-image:url('data:image/svg+xml;utf8, ')}.video-js .vjs-touch-overlay.skip.reverse{background-position:20% center;background-image:url('data:image/svg+xml;utf8, ')}.video-js .vjs-touch-overlay .vjs-play-control{top:50%;left:50%;transform:translate(-50%, -50%);position:absolute;width:30%;height:80%;pointer-events:none;opacity:0;transition:opacity 0.3s ease}.video-js .vjs-touch-overlay .vjs-play-control .vjs-icon-placeholder::before{content:'';background-size:60%;background-position:center center;background-repeat:no-repeat;background-image:url('data:image/svg+xml;utf8, ')}.video-js .vjs-touch-overlay .vjs-play-control.vjs-paused .vjs-icon-placeholder::before{content:'';background-image:url('data:image/svg+xml;utf8, ')}.video-js .vjs-touch-overlay .vjs-play-control.vjs-ended .vjs-icon-placeholder::before{content:'';background-image:url('data:image/svg+xml;utf8, ')}.video-js .vjs-touch-overlay.show-play-toggle .vjs-play-control{opacity:1;pointer-events:auto}.video-js.vjs-mobile-ui-disable-end.vjs-ended .vjs-touch-overlay{display:none}
diff --git a/assets/css/videojs-vr.css b/assets/css/videojs-vr.css
new file mode 100644
index 00000000..6e850c2f
--- /dev/null
+++ b/assets/css/videojs-vr.css
@@ -0,0 +1 @@
+.video-js .vjs-big-vr-play-button{width:100px;height:100px;background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='360' height='360' viewBox='0 0 360 360'%3E%3Cpath fill='%23FFF' d='M334.883 275.78l-6.374-36.198-6.375-36.2-28.16 23.62-28.164 23.62 25.837 9.41C266.247 296.544 224 320.5 176.25 320.5c-77.47 0-140.5-63.03-140.5-140.5 0-77.472 63.03-140.5 140.5-140.5 53.428 0 99.98 29.978 123.733 73.993l13.304-6.923C287.025 57.76 235.45 24.5 176.25 24.5c-85.743 0-155.5 69.757-155.5 155.5 0 85.742 69.757 155.5 155.5 155.5 54.253 0 102.09-27.94 129.922-70.177l28.71 10.457z'/%3E%3Cpath fill='%23FFF' d='M314.492 175.167c-12.98 0-23.54-10.56-23.54-23.54s10.56-23.54 23.54-23.54c12.98 0 23.54 10.56 23.54 23.54s-10.56 23.54-23.54 23.54zm0-38.08c-8.018 0-14.54 6.522-14.54 14.54s6.522 14.54 14.54 14.54c8.017 0 14.54-6.522 14.54-14.54s-6.523-14.54-14.54-14.54z'/%3E%3Cg fill='%23FFF'%3E%3Cpath d='M88.76 173.102h9.395c4.74-.042 8.495-1.27 11.268-3.682 2.77-2.412 4.157-5.903 4.157-10.474 0-4.4-1.153-7.817-3.46-10.25-2.307-2.434-5.83-3.65-10.568-3.65-4.147 0-7.554 1.195-10.22 3.585-2.666 2.392-4 5.514-4 9.364H69.908c0-4.74 1.26-9.055 3.776-12.95 2.518-3.892 6.03-6.928 10.537-9.108 4.508-2.18 9.554-3.27 15.14-3.27 9.225 0 16.472 2.318 21.74 6.952 5.27 4.634 7.903 11.077 7.903 19.33 0 4.147-1.323 8.05-3.967 11.71-2.646 3.66-6.062 6.422-10.252 8.284 5.078 1.736 8.94 4.465 11.584 8.19s3.968 8.166 3.968 13.33c0 8.294-2.847 14.895-8.538 19.804s-13.17 7.363-22.438 7.363c-8.887 0-16.166-2.37-21.836-7.11-5.67-4.74-8.506-11.045-8.506-18.916h15.425c0 4.062 1.365 7.363 4.094 9.902 2.73 2.54 6.4 3.81 11.014 3.81 4.782 0 8.55-1.27 11.3-3.81s4.126-6.22 4.126-11.045c0-4.865-1.44-8.61-4.316-11.235-2.878-2.623-7.152-3.936-12.822-3.936H88.76V173.1zM187.598 133.493v12.76h-1.904c-8.633.126-15.53 2.497-20.693 7.108-5.162 4.614-8.23 11.152-9.203 19.615 4.95-5.205 11.277-7.808 18.98-7.808 8.166 0 14.608 2.878 19.328 8.633 4.718 5.755 7.077 13.182 7.077 22.28 0 9.395-2.76 17.002-8.284 22.82-5.52 5.818-12.77 8.73-21.74 8.73-9.226 0-16.705-3.407-22.44-10.222-5.733-6.812-8.6-15.742-8.6-26.787v-5.267c0-16.208 3.945-28.903 11.84-38.086 7.89-9.182 19.242-13.774 34.054-13.774h1.586zM171.03 177.61c-3.386 0-6.485.95-9.3 2.855-2.814 1.904-4.877 4.443-6.188 7.617v4.697c0 6.854 1.438 12.304 4.316 16.345 2.877 4.04 6.602 6.062 11.172 6.062s8.188-1.715 10.854-5.143 4-7.934 4-13.52-1.355-10.135-4.063-13.648c-2.708-3.51-6.304-5.267-10.79-5.267zM271.136 187.447c0 13.29-2.486 23.307-7.46 30.057s-12.535 10.125-22.69 10.125c-9.988 0-17.51-3.292-22.566-9.872-5.058-6.58-7.65-16.323-7.776-29.23V172.53c0-13.287 2.485-23.252 7.458-29.896 4.973-6.643 12.558-9.966 22.757-9.966 10.112 0 17.655 3.237 22.63 9.712 4.97 6.475 7.52 16.166 7.647 29.072v15.995zm-15.425-17.265c0-8.674-1.185-15.033-3.554-19.075-2.37-4.04-6.137-6.062-11.3-6.062-5.035 0-8.738 1.915-11.107 5.745-2.37 3.83-3.62 9.807-3.746 17.932v20.948c0 8.633 1.206 15.064 3.618 19.297s6.2 6.348 11.362 6.348c4.95 0 8.61-1.957 10.98-5.87 2.37-3.915 3.62-10.04 3.746-18.378v-20.885z'/%3E%3C/g%3E%3C/svg%3E");background-size:contain;background-color:rgba(0,0,0,0.5)}.video-js .vjs-big-vr-play-button .vjs-icon-placeholder{display:none}:hover.video-js .vjs-big-vr-play-button{-webkit-transition:border-color 0.4s,outline 0.4s,background-color 0.4s;-moz-transition:border-color 0.4s,outline 0.4s,background-color 0.4s;-ms-transition:border-color 0.4s,outline 0.4s,background-color 0.4s;-o-transition:border-color 0.4s,outline 0.4s,background-color 0.4s;transition:border-color 0.4s,outline 0.4s,background-color 0.4s}.video-js .vjs-big-vr-play-button::before{content:''}.video-js canvas{cursor:move}.video-js .vjs-button-vr .vjs-icon-placeholder{height:30px;width:30px;display:inline-block;background:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI0ZGRkZGRiI+CiAgICA8cGF0aCBkPSJNMjAuNzQgNkgzLjIxQzIuNTUgNiAyIDYuNTcgMiA3LjI4djEwLjQ0YzAgLjcuNTUgMS4yOCAxLjIzIDEuMjhoNC43OWMuNTIgMCAuOTYtLjMzIDEuMTQtLjc5bDEuNC0zLjQ4Yy4yMy0uNTkuNzktMS4wMSAxLjQ0LTEuMDFzMS4yMS40MiAxLjQ1IDEuMDFsMS4zOSAzLjQ4Yy4xOS40Ni42My43OSAxLjExLjc5aDQuNzljLjcxIDAgMS4yNi0uNTcgMS4yNi0xLjI4VjcuMjhjMC0uNy0uNTUtMS4yOC0xLjI2LTEuMjh6TTcuNSAxNC42MmMtMS4xNyAwLTIuMTMtLjk1LTIuMTMtMi4xMiAwLTEuMTcuOTYtMi4xMyAyLjEzLTIuMTMgMS4xOCAwIDIuMTIuOTYgMi4xMiAyLjEzcy0uOTUgMi4xMi0yLjEyIDIuMTJ6bTkgMGMtMS4xNyAwLTIuMTMtLjk1LTIuMTMtMi4xMiAwLTEuMTcuOTYtMi4xMyAyLjEzLTIuMTNzMi4xMi45NiAyLjEyIDIuMTMtLjk1IDIuMTItMi4xMiAyLjEyeiIvPgogICAgPHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyNHYyNEgwVjB6Ii8+Cjwvc3ZnPgo=) no-repeat left center}
diff --git a/assets/invidious-colored-vector.svg b/assets/invidious-colored-vector.svg
new file mode 100644
index 00000000..741a8fd8
--- /dev/null
+++ b/assets/invidious-colored-vector.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/assets/js/handlers.js b/assets/js/handlers.js
index b3da8d9b..1498f39a 100644
--- a/assets/js/handlers.js
+++ b/assets/js/handlers.js
@@ -22,7 +22,8 @@
break;
case 'get_youtube_replies':
var load_more = e.getAttribute('data-load-more') !== null;
- get_youtube_replies(e, load_more);
+ var load_replies = e.getAttribute('data-load-replies') !== null;
+ get_youtube_replies(e, load_more, load_replies);
break;
case 'toggle_parent':
toggle_parent(e);
diff --git a/assets/js/player.js b/assets/js/player.js
index 1c6e336c..a461c53d 100644
--- a/assets/js/player.js
+++ b/assets/js/player.js
@@ -14,6 +14,7 @@ var options = {
'durationDisplay',
'progressControl',
'remainingTimeDisplay',
+ 'Spacer',
'captionsButton',
'qualitySelector',
'playbackRateMenuButton',
@@ -73,6 +74,55 @@ if (location.pathname.startsWith('/embed/')) {
});
}
+// Detect mobile users and initalize mobileUi for better UX
+// Detection code taken from https://stackoverflow.com/a/20293441
+
+function isMobile() {
+ try{ document.createEvent("TouchEvent"); return true; }
+ catch(e){ return false; }
+}
+
+if (isMobile()) {
+ player.mobileUi();
+
+ buttons = ["playToggle", "volumePanel", "captionsButton"];
+
+ if (video_data.params.quality !== 'dash') {
+ buttons.push("qualitySelector")
+ }
+
+ // Create new control bar object for operation buttons
+ const ControlBar = videojs.getComponent("controlBar");
+ let operations_bar = new ControlBar(player, {
+ children: [],
+ playbackRates: [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0]
+ });
+ buttons.slice(1).forEach(child => operations_bar.addChild(child))
+
+ // Remove operation buttons from primary control bar
+ primary_control_bar = player.getChild("controlBar");
+ buttons.forEach(child => primary_control_bar.removeChild(child));
+
+ operations_bar_element = operations_bar.el();
+ operations_bar_element.className += " mobile-operations-bar"
+ player.addChild(operations_bar)
+
+ // Playback menu doesn't work when its initalized outside of the primary control bar
+ playback_element = document.getElementsByClassName("vjs-playback-rate")[0]
+ operations_bar_element.append(playback_element)
+
+ // The share and http source selector element can't be fetched till the players ready.
+ player.one("playing", () => {
+ share_element = document.getElementsByClassName("vjs-share-control")[0]
+ operations_bar_element.append(share_element)
+
+ if (video_data.params.quality === 'dash') {
+ http_source_selector = document.getElementsByClassName("vjs-http-source-selector vjs-menu-button")[0]
+ operations_bar_element.append(http_source_selector)
+ }
+ })
+}
+
player.on('error', function (event) {
if (player.error().code === 2 || player.error().code === 4) {
setTimeout(function (event) {
@@ -98,6 +148,17 @@ player.on('error', function (event) {
}
});
+// Enable VR video support
+if (!video_data.params.listen && video_data.vr && video_data.params.vr_mode) {
+ player.crossOrigin("anonymous")
+ switch (video_data.projection_type) {
+ case "EQUIRECTANGULAR":
+ player.vr({projection: "equirectangular"});
+ default: // Should only be "MESH" but we'll use this as a fallback.
+ player.vr({projection: "EAC"});
+ }
+}
+
// Add markers
if (video_data.params.video_start > 0 || video_data.params.video_end > 0) {
var markers = [{ time: video_data.params.video_start, text: 'Start' }];
@@ -566,3 +627,20 @@ if (navigator.vendor == "Apple Computer, Inc." && video_data.params.listen) {
});
});
}
+
+// Watch on Invidious link
+if (window.location.pathname.startsWith("/embed/")) {
+ const Button = videojs.getComponent('Button');
+ let watch_on_invidious_button = new Button(player);
+
+ // Create hyperlink for current instance
+ redirect_element = document.createElement("a");
+ redirect_element.setAttribute("href", `http://${window.location.host}/watch?v=${window.location.pathname.replace("/embed/","")}`)
+ redirect_element.appendChild(document.createTextNode("Invidious"))
+
+ watch_on_invidious_button.el().appendChild(redirect_element)
+ watch_on_invidious_button.addClass("watch-on-invidious")
+
+ cb = player.getChild('ControlBar')
+ cb.addChild(watch_on_invidious_button)
+};
diff --git a/assets/js/videojs-mobile-ui.min.js b/assets/js/videojs-mobile-ui.min.js
new file mode 100644
index 00000000..e624fbe2
--- /dev/null
+++ b/assets/js/videojs-mobile-ui.min.js
@@ -0,0 +1,7 @@
+/**
+ * videojs-mobile-ui
+ * @version 0.5.2
+ * @copyright 2021 mister-ben
+ * @license MIT
+ */
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("video.js"),require("global/window")):"function"==typeof define&&define.amd?define(["video.js","global/window"],t):e.videojsMobileUi=t(e.videojs,e.window)}(this,function(e,t){"use strict";e=e&&e.hasOwnProperty("default")?e.default:e,t=t&&t.hasOwnProperty("default")?t.default:t;var n=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},o=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t},i=e.getComponent("Component"),r=e.dom||e,a=function(e){function i(t,r){n(this,i);var a=o(this,e.call(this,t,r));return a.seekSeconds=r.seekSeconds,a.tapTimeout=r.tapTimeout,a.addChild("playToggle",{}),t.on(["playing","userinactive"],function(e){a.removeClass("show-play-toggle")}),0===a.player_.options_.inactivityTimeout&&(a.player_.options_.inactivityTimeout=5e3),a.enable(),a}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(i,e),i.prototype.createEl=function(){return r.createEl("div",{className:"vjs-touch-overlay",tabIndex:-1})},i.prototype.handleTap=function(e){var n=this;e.target===this.el_&&(e.preventDefault(),this.firstTapCaptured?(this.firstTapCaptured=!1,this.timeout&&t.clearTimeout(this.timeout),this.handleDoubleTap(e)):(this.firstTapCaptured=!0,this.timeout=t.setTimeout(function(){n.firstTapCaptured=!1,n.handleSingleTap(e)},this.tapTimeout)))},i.prototype.handleSingleTap=function(e){this.removeClass("skip"),this.toggleClass("show-play-toggle")},i.prototype.handleDoubleTap=function(e){var n=this,o=this.el_.getBoundingClientRect(),i=e.changedTouches[0].clientX-o.left;if(i<.4*o.width)this.player_.currentTime(Math.max(0,this.player_.currentTime()-this.seekSeconds)),this.addClass("reverse");else{if(!(i>o.width-.4*o.width))return;this.player_.currentTime(Math.min(this.player_.duration(),this.player_.currentTime()+this.seekSeconds)),this.removeClass("reverse")}this.removeClass("show-play-toggle"),this.removeClass("skip"),t.requestAnimationFrame(function(){n.addClass("skip")})},i.prototype.enable=function(){this.firstTapCaptured=!1,this.on("touchend",this.handleTap)},i.prototype.disable=function(){this.off("touchend",this.handleTap)},i}(i);i.registerComponent("TouchOverlay",a);var s={fullscreen:{enterOnRotate:!0,exitOnRotate:!0,lockOnRotate:!0,iOS:!1},touchControls:{seekSeconds:10,tapTimeout:300,disableOnEnd:!1}},l=t.screen,u=function(n,o){n.addClass("vjs-mobile-ui"),(o.touchControls.disableOnEnd||"function"==typeof n.endscreen)&&n.addClass("vjs-mobile-ui-disable-end"),o.fullscreen.iOS&&e.browser.IS_IOS&&e.browser.IOS_VERSION>9&&!n.el_.ownerDocument.querySelector(".bc-iframe")&&(n.tech_.el_.setAttribute("playsinline","playsinline"),n.tech_.supportsFullScreen=function(){return!1});var i=void 0,r=e.VERSION.split("."),a=parseInt(r[0],10),s=parseInt(r[1],10);i=a<7||7===a&&s<7?Array.prototype.indexOf.call(n.el_.children,n.getChild("ControlBar").el_):n.children_.indexOf(n.getChild("ControlBar")),n.addChild("TouchOverlay",o.touchControls,i);var u=!1,c=function(){var i="number"==typeof t.orientation?t.orientation:l&&l.orientation&&l.orientation.angle?t.orientation:(e.log("angle unknown"),0);90!==i&&270!==i&&-90!==i||!o.enterOnRotate||!1===n.paused()&&(n.requestFullscreen(),o.fullscreen.lockOnRotate&&l.orientation&&l.orientation.lock&&l.orientation.lock("landscape").then(function(){u=!0}).catch(function(){e.log("orientation lock not allowed")})),0!==i&&180!==i||!o.exitOnRotate||n.isFullscreen()&&n.exitFullscreen()};e.browser.IS_IOS?t.addEventListener("orientationchange",c):l.orientation&&(l.orientation.onchange=c),n.on("ended",function(e){!0===u&&(l.orientation.unlock(),u=!1)})},c=function(){var t=this,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};(n.forceForTesting||e.browser.IS_ANDROID||e.browser.IS_IOS)&&this.ready(function(){u(t,e.mergeOptions(s,n))})};return(e.registerPlugin||e.plugin)("mobileUi",c),c.VERSION="0.5.2",c});
\ No newline at end of file
diff --git a/assets/js/videojs-vr.js b/assets/js/videojs-vr.js
new file mode 100644
index 00000000..f857f61d
--- /dev/null
+++ b/assets/js/videojs-vr.js
@@ -0,0 +1,52730 @@
+/*! @name videojs-vr @version 1.7.1 @license Apache-2.0 */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('global/window'), require('global/document'), require('video.js')) :
+ typeof define === 'function' && define.amd ? define(['global/window', 'global/document', 'video.js'], factory) :
+ (global = global || self, global.videojsVr = factory(global.window, global.document, global.videojs));
+}(this, function (window$1, document$1, videojs) { 'use strict';
+
+ window$1 = window$1 && window$1.hasOwnProperty('default') ? window$1['default'] : window$1;
+ document$1 = document$1 && document$1.hasOwnProperty('default') ? document$1['default'] : document$1;
+ videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs;
+
+ function _assertThisInitialized(self) {
+ if (self === void 0) {
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+ }
+
+ return self;
+ }
+
+ var assertThisInitialized = _assertThisInitialized;
+
+ function _inheritsLoose(subClass, superClass) {
+ subClass.prototype = Object.create(superClass.prototype);
+ subClass.prototype.constructor = subClass;
+ subClass.__proto__ = superClass;
+ }
+
+ var inheritsLoose = _inheritsLoose;
+
+ var version = "1.7.1";
+
+ var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+ function unwrapExports (x) {
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
+ }
+
+ function createCommonjsModule(fn, module) {
+ return module = { exports: {} }, fn(module, module.exports), module.exports;
+ }
+
+ var webvrPolyfill = createCommonjsModule(function (module, exports) {
+ /**
+ * @license
+ * webvr-polyfill
+ * Copyright (c) 2015-2017 Google
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /**
+ * @license
+ * cardboard-vr-display
+ * Copyright (c) 2015-2017 Google
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /**
+ * @license
+ * webvr-polyfill-dpdb
+ * Copyright (c) 2017 Google
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /**
+ * @license
+ * wglu-preserve-state
+ * Copyright (c) 2016, Brandon Jones.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+ /**
+ * @license
+ * nosleep.js
+ * Copyright (c) 2017, Rich Tibbett
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+ (function (global, factory) {
+ module.exports = factory() ;
+ }(commonjsGlobal, (function () {
+ var commonjsGlobal$1 = typeof window !== 'undefined' ? window : typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : typeof self !== 'undefined' ? self : {};
+
+
+
+ function unwrapExports (x) {
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
+ }
+
+ function createCommonjsModule(fn, module) {
+ return module = { exports: {} }, fn(module, module.exports), module.exports;
+ }
+
+ var race = function race(promises) {
+ if (Promise.race) {
+ return Promise.race(promises);
+ }
+ return new Promise(function (resolve, reject) {
+ for (var i = 0; i < promises.length; i++) {
+ promises[i].then(resolve, reject);
+ }
+ });
+ };
+
+ var isMobile = function isMobile() {
+ return (/Android/i.test(navigator.userAgent) || /iPhone|iPad|iPod/i.test(navigator.userAgent)
+ );
+ };
+ var copyArray = function copyArray(source, dest) {
+ for (var i = 0, n = source.length; i < n; i++) {
+ dest[i] = source[i];
+ }
+ };
+ var extend = function extend(dest, src) {
+ for (var key in src) {
+ if (src.hasOwnProperty(key)) {
+ dest[key] = src[key];
+ }
+ }
+ return dest;
+ };
+
+ var cardboardVrDisplay = createCommonjsModule(function (module, exports) {
+ /**
+ * @license
+ * cardboard-vr-display
+ * Copyright (c) 2015-2017 Google
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ /**
+ * @license
+ * gl-preserve-state
+ * Copyright (c) 2016, Brandon Jones.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+ /**
+ * @license
+ * webvr-polyfill-dpdb
+ * Copyright (c) 2015-2017 Google
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ /**
+ * @license
+ * nosleep.js
+ * Copyright (c) 2017, Rich Tibbett
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+ (function (global, factory) {
+ module.exports = factory();
+ }(commonjsGlobal$1, (function () { var classCallCheck = function (instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+ };
+ var createClass = function () {
+ function defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ("value" in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+ return function (Constructor, protoProps, staticProps) {
+ if (protoProps) defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) defineProperties(Constructor, staticProps);
+ return Constructor;
+ };
+ }();
+ var slicedToArray = function () {
+ function sliceIterator(arr, i) {
+ var _arr = [];
+ var _n = true;
+ var _d = false;
+ var _e = undefined;
+ try {
+ for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
+ _arr.push(_s.value);
+ if (i && _arr.length === i) break;
+ }
+ } catch (err) {
+ _d = true;
+ _e = err;
+ } finally {
+ try {
+ if (!_n && _i["return"]) _i["return"]();
+ } finally {
+ if (_d) throw _e;
+ }
+ }
+ return _arr;
+ }
+ return function (arr, i) {
+ if (Array.isArray(arr)) {
+ return arr;
+ } else if (Symbol.iterator in Object(arr)) {
+ return sliceIterator(arr, i);
+ } else {
+ throw new TypeError("Invalid attempt to destructure non-iterable instance");
+ }
+ };
+ }();
+ var MIN_TIMESTEP = 0.001;
+ var MAX_TIMESTEP = 1;
+ var base64 = function base64(mimeType, _base) {
+ return 'data:' + mimeType + ';base64,' + _base;
+ };
+ var lerp = function lerp(a, b, t) {
+ return a + (b - a) * t;
+ };
+ var isIOS = function () {
+ var isIOS = /iPad|iPhone|iPod/.test(navigator.platform);
+ return function () {
+ return isIOS;
+ };
+ }();
+ var isWebViewAndroid = function () {
+ var isWebViewAndroid = navigator.userAgent.indexOf('Version') !== -1 && navigator.userAgent.indexOf('Android') !== -1 && navigator.userAgent.indexOf('Chrome') !== -1;
+ return function () {
+ return isWebViewAndroid;
+ };
+ }();
+ var isSafari = function () {
+ var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
+ return function () {
+ return isSafari;
+ };
+ }();
+ var isFirefoxAndroid = function () {
+ var isFirefoxAndroid = navigator.userAgent.indexOf('Firefox') !== -1 && navigator.userAgent.indexOf('Android') !== -1;
+ return function () {
+ return isFirefoxAndroid;
+ };
+ }();
+ var getChromeVersion = function () {
+ var match = navigator.userAgent.match(/.*Chrome\/([0-9]+)/);
+ var value = match ? parseInt(match[1], 10) : null;
+ return function () {
+ return value;
+ };
+ }();
+ var isChromeWithoutDeviceMotion = function () {
+ var value = false;
+ if (getChromeVersion() === 65) {
+ var match = navigator.userAgent.match(/.*Chrome\/([0-9\.]*)/);
+ if (match) {
+ var _match$1$split = match[1].split('.'),
+ _match$1$split2 = slicedToArray(_match$1$split, 4),
+ major = _match$1$split2[0],
+ minor = _match$1$split2[1],
+ branch = _match$1$split2[2],
+ build = _match$1$split2[3];
+ value = parseInt(branch, 10) === 3325 && parseInt(build, 10) < 148;
+ }
+ }
+ return function () {
+ return value;
+ };
+ }();
+ var isR7 = function () {
+ var isR7 = navigator.userAgent.indexOf('R7 Build') !== -1;
+ return function () {
+ return isR7;
+ };
+ }();
+ var isLandscapeMode = function isLandscapeMode() {
+ var rtn = window.orientation == 90 || window.orientation == -90;
+ return isR7() ? !rtn : rtn;
+ };
+ var isTimestampDeltaValid = function isTimestampDeltaValid(timestampDeltaS) {
+ if (isNaN(timestampDeltaS)) {
+ return false;
+ }
+ if (timestampDeltaS <= MIN_TIMESTEP) {
+ return false;
+ }
+ if (timestampDeltaS > MAX_TIMESTEP) {
+ return false;
+ }
+ return true;
+ };
+ var getScreenWidth = function getScreenWidth() {
+ return Math.max(window.screen.width, window.screen.height) * window.devicePixelRatio;
+ };
+ var getScreenHeight = function getScreenHeight() {
+ return Math.min(window.screen.width, window.screen.height) * window.devicePixelRatio;
+ };
+ var requestFullscreen = function requestFullscreen(element) {
+ if (isWebViewAndroid()) {
+ return false;
+ }
+ if (element.requestFullscreen) {
+ element.requestFullscreen();
+ } else if (element.webkitRequestFullscreen) {
+ element.webkitRequestFullscreen();
+ } else if (element.mozRequestFullScreen) {
+ element.mozRequestFullScreen();
+ } else if (element.msRequestFullscreen) {
+ element.msRequestFullscreen();
+ } else {
+ return false;
+ }
+ return true;
+ };
+ var exitFullscreen = function exitFullscreen() {
+ if (document.exitFullscreen) {
+ document.exitFullscreen();
+ } else if (document.webkitExitFullscreen) {
+ document.webkitExitFullscreen();
+ } else if (document.mozCancelFullScreen) {
+ document.mozCancelFullScreen();
+ } else if (document.msExitFullscreen) {
+ document.msExitFullscreen();
+ } else {
+ return false;
+ }
+ return true;
+ };
+ var getFullscreenElement = function getFullscreenElement() {
+ return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
+ };
+ var linkProgram = function linkProgram(gl, vertexSource, fragmentSource, attribLocationMap) {
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, vertexSource);
+ gl.compileShader(vertexShader);
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, fragmentSource);
+ gl.compileShader(fragmentShader);
+ var program = gl.createProgram();
+ gl.attachShader(program, vertexShader);
+ gl.attachShader(program, fragmentShader);
+ for (var attribName in attribLocationMap) {
+ gl.bindAttribLocation(program, attribLocationMap[attribName], attribName);
+ }gl.linkProgram(program);
+ gl.deleteShader(vertexShader);
+ gl.deleteShader(fragmentShader);
+ return program;
+ };
+ var getProgramUniforms = function getProgramUniforms(gl, program) {
+ var uniforms = {};
+ var uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
+ var uniformName = '';
+ for (var i = 0; i < uniformCount; i++) {
+ var uniformInfo = gl.getActiveUniform(program, i);
+ uniformName = uniformInfo.name.replace('[0]', '');
+ uniforms[uniformName] = gl.getUniformLocation(program, uniformName);
+ }
+ return uniforms;
+ };
+ var orthoMatrix = function orthoMatrix(out, left, right, bottom, top, near, far) {
+ var lr = 1 / (left - right),
+ bt = 1 / (bottom - top),
+ nf = 1 / (near - far);
+ out[0] = -2 * lr;
+ out[1] = 0;
+ out[2] = 0;
+ out[3] = 0;
+ out[4] = 0;
+ out[5] = -2 * bt;
+ out[6] = 0;
+ out[7] = 0;
+ out[8] = 0;
+ out[9] = 0;
+ out[10] = 2 * nf;
+ out[11] = 0;
+ out[12] = (left + right) * lr;
+ out[13] = (top + bottom) * bt;
+ out[14] = (far + near) * nf;
+ out[15] = 1;
+ return out;
+ };
+ var isMobile = function isMobile() {
+ var check = false;
+ (function (a) {
+ if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true;
+ })(navigator.userAgent || navigator.vendor || window.opera);
+ return check;
+ };
+ var extend = function extend(dest, src) {
+ for (var key in src) {
+ if (src.hasOwnProperty(key)) {
+ dest[key] = src[key];
+ }
+ }
+ return dest;
+ };
+ var safariCssSizeWorkaround = function safariCssSizeWorkaround(canvas) {
+ if (isIOS()) {
+ var width = canvas.style.width;
+ var height = canvas.style.height;
+ canvas.style.width = parseInt(width) + 1 + 'px';
+ canvas.style.height = parseInt(height) + 'px';
+ setTimeout(function () {
+ canvas.style.width = width;
+ canvas.style.height = height;
+ }, 100);
+ }
+ window.canvas = canvas;
+ };
+ var frameDataFromPose = function () {
+ var piOver180 = Math.PI / 180.0;
+ var rad45 = Math.PI * 0.25;
+ function mat4_perspectiveFromFieldOfView(out, fov, near, far) {
+ var upTan = Math.tan(fov ? fov.upDegrees * piOver180 : rad45),
+ downTan = Math.tan(fov ? fov.downDegrees * piOver180 : rad45),
+ leftTan = Math.tan(fov ? fov.leftDegrees * piOver180 : rad45),
+ rightTan = Math.tan(fov ? fov.rightDegrees * piOver180 : rad45),
+ xScale = 2.0 / (leftTan + rightTan),
+ yScale = 2.0 / (upTan + downTan);
+ out[0] = xScale;
+ out[1] = 0.0;
+ out[2] = 0.0;
+ out[3] = 0.0;
+ out[4] = 0.0;
+ out[5] = yScale;
+ out[6] = 0.0;
+ out[7] = 0.0;
+ out[8] = -((leftTan - rightTan) * xScale * 0.5);
+ out[9] = (upTan - downTan) * yScale * 0.5;
+ out[10] = far / (near - far);
+ out[11] = -1.0;
+ out[12] = 0.0;
+ out[13] = 0.0;
+ out[14] = far * near / (near - far);
+ out[15] = 0.0;
+ return out;
+ }
+ function mat4_fromRotationTranslation(out, q, v) {
+ var x = q[0],
+ y = q[1],
+ z = q[2],
+ w = q[3],
+ x2 = x + x,
+ y2 = y + y,
+ z2 = z + z,
+ xx = x * x2,
+ xy = x * y2,
+ xz = x * z2,
+ yy = y * y2,
+ yz = y * z2,
+ zz = z * z2,
+ wx = w * x2,
+ wy = w * y2,
+ wz = w * z2;
+ out[0] = 1 - (yy + zz);
+ out[1] = xy + wz;
+ out[2] = xz - wy;
+ out[3] = 0;
+ out[4] = xy - wz;
+ out[5] = 1 - (xx + zz);
+ out[6] = yz + wx;
+ out[7] = 0;
+ out[8] = xz + wy;
+ out[9] = yz - wx;
+ out[10] = 1 - (xx + yy);
+ out[11] = 0;
+ out[12] = v[0];
+ out[13] = v[1];
+ out[14] = v[2];
+ out[15] = 1;
+ return out;
+ }
+ function mat4_translate(out, a, v) {
+ var x = v[0],
+ y = v[1],
+ z = v[2],
+ a00,
+ a01,
+ a02,
+ a03,
+ a10,
+ a11,
+ a12,
+ a13,
+ a20,
+ a21,
+ a22,
+ a23;
+ if (a === out) {
+ out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
+ out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
+ out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
+ out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
+ } else {
+ a00 = a[0];a01 = a[1];a02 = a[2];a03 = a[3];
+ a10 = a[4];a11 = a[5];a12 = a[6];a13 = a[7];
+ a20 = a[8];a21 = a[9];a22 = a[10];a23 = a[11];
+ out[0] = a00;out[1] = a01;out[2] = a02;out[3] = a03;
+ out[4] = a10;out[5] = a11;out[6] = a12;out[7] = a13;
+ out[8] = a20;out[9] = a21;out[10] = a22;out[11] = a23;
+ out[12] = a00 * x + a10 * y + a20 * z + a[12];
+ out[13] = a01 * x + a11 * y + a21 * z + a[13];
+ out[14] = a02 * x + a12 * y + a22 * z + a[14];
+ out[15] = a03 * x + a13 * y + a23 * z + a[15];
+ }
+ return out;
+ }
+ function mat4_invert(out, a) {
+ var a00 = a[0],
+ a01 = a[1],
+ a02 = a[2],
+ a03 = a[3],
+ a10 = a[4],
+ a11 = a[5],
+ a12 = a[6],
+ a13 = a[7],
+ a20 = a[8],
+ a21 = a[9],
+ a22 = a[10],
+ a23 = a[11],
+ a30 = a[12],
+ a31 = a[13],
+ a32 = a[14],
+ a33 = a[15],
+ b00 = a00 * a11 - a01 * a10,
+ b01 = a00 * a12 - a02 * a10,
+ b02 = a00 * a13 - a03 * a10,
+ b03 = a01 * a12 - a02 * a11,
+ b04 = a01 * a13 - a03 * a11,
+ b05 = a02 * a13 - a03 * a12,
+ b06 = a20 * a31 - a21 * a30,
+ b07 = a20 * a32 - a22 * a30,
+ b08 = a20 * a33 - a23 * a30,
+ b09 = a21 * a32 - a22 * a31,
+ b10 = a21 * a33 - a23 * a31,
+ b11 = a22 * a33 - a23 * a32,
+ det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
+ if (!det) {
+ return null;
+ }
+ det = 1.0 / det;
+ out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
+ out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
+ out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
+ out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
+ out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
+ out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
+ out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
+ out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
+ out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
+ out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
+ out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
+ out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
+ out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
+ out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
+ out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
+ out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
+ return out;
+ }
+ var defaultOrientation = new Float32Array([0, 0, 0, 1]);
+ var defaultPosition = new Float32Array([0, 0, 0]);
+ function updateEyeMatrices(projection, view, pose, fov, offset, vrDisplay) {
+ mat4_perspectiveFromFieldOfView(projection, fov || null, vrDisplay.depthNear, vrDisplay.depthFar);
+ var orientation = pose.orientation || defaultOrientation;
+ var position = pose.position || defaultPosition;
+ mat4_fromRotationTranslation(view, orientation, position);
+ if (offset) mat4_translate(view, view, offset);
+ mat4_invert(view, view);
+ }
+ return function (frameData, pose, vrDisplay) {
+ if (!frameData || !pose) return false;
+ frameData.pose = pose;
+ frameData.timestamp = pose.timestamp;
+ updateEyeMatrices(frameData.leftProjectionMatrix, frameData.leftViewMatrix, pose, vrDisplay._getFieldOfView("left"), vrDisplay._getEyeOffset("left"), vrDisplay);
+ updateEyeMatrices(frameData.rightProjectionMatrix, frameData.rightViewMatrix, pose, vrDisplay._getFieldOfView("right"), vrDisplay._getEyeOffset("right"), vrDisplay);
+ return true;
+ };
+ }();
+ var isInsideCrossOriginIFrame = function isInsideCrossOriginIFrame() {
+ var isFramed = window.self !== window.top;
+ var refOrigin = getOriginFromUrl(document.referrer);
+ var thisOrigin = getOriginFromUrl(window.location.href);
+ return isFramed && refOrigin !== thisOrigin;
+ };
+ var getOriginFromUrl = function getOriginFromUrl(url) {
+ var domainIdx;
+ var protoSepIdx = url.indexOf("://");
+ if (protoSepIdx !== -1) {
+ domainIdx = protoSepIdx + 3;
+ } else {
+ domainIdx = 0;
+ }
+ var domainEndIdx = url.indexOf('/', domainIdx);
+ if (domainEndIdx === -1) {
+ domainEndIdx = url.length;
+ }
+ return url.substring(0, domainEndIdx);
+ };
+ var getQuaternionAngle = function getQuaternionAngle(quat) {
+ if (quat.w > 1) {
+ console.warn('getQuaternionAngle: w > 1');
+ return 0;
+ }
+ var angle = 2 * Math.acos(quat.w);
+ return angle;
+ };
+ var warnOnce = function () {
+ var observedWarnings = {};
+ return function (key, message) {
+ if (observedWarnings[key] === undefined) {
+ console.warn('webvr-polyfill: ' + message);
+ observedWarnings[key] = true;
+ }
+ };
+ }();
+ var deprecateWarning = function deprecateWarning(deprecated, suggested) {
+ var alternative = suggested ? 'Please use ' + suggested + ' instead.' : '';
+ warnOnce(deprecated, deprecated + ' has been deprecated. ' + 'This may not work on native WebVR displays. ' + alternative);
+ };
+ function WGLUPreserveGLState(gl, bindings, callback) {
+ if (!bindings) {
+ callback(gl);
+ return;
+ }
+ var boundValues = [];
+ var activeTexture = null;
+ for (var i = 0; i < bindings.length; ++i) {
+ var binding = bindings[i];
+ switch (binding) {
+ case gl.TEXTURE_BINDING_2D:
+ case gl.TEXTURE_BINDING_CUBE_MAP:
+ var textureUnit = bindings[++i];
+ if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) {
+ console.error("TEXTURE_BINDING_2D or TEXTURE_BINDING_CUBE_MAP must be followed by a valid texture unit");
+ boundValues.push(null, null);
+ break;
+ }
+ if (!activeTexture) {
+ activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE);
+ }
+ gl.activeTexture(textureUnit);
+ boundValues.push(gl.getParameter(binding), null);
+ break;
+ case gl.ACTIVE_TEXTURE:
+ activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE);
+ boundValues.push(null);
+ break;
+ default:
+ boundValues.push(gl.getParameter(binding));
+ break;
+ }
+ }
+ callback(gl);
+ for (var i = 0; i < bindings.length; ++i) {
+ var binding = bindings[i];
+ var boundValue = boundValues[i];
+ switch (binding) {
+ case gl.ACTIVE_TEXTURE:
+ break;
+ case gl.ARRAY_BUFFER_BINDING:
+ gl.bindBuffer(gl.ARRAY_BUFFER, boundValue);
+ break;
+ case gl.COLOR_CLEAR_VALUE:
+ gl.clearColor(boundValue[0], boundValue[1], boundValue[2], boundValue[3]);
+ break;
+ case gl.COLOR_WRITEMASK:
+ gl.colorMask(boundValue[0], boundValue[1], boundValue[2], boundValue[3]);
+ break;
+ case gl.CURRENT_PROGRAM:
+ gl.useProgram(boundValue);
+ break;
+ case gl.ELEMENT_ARRAY_BUFFER_BINDING:
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, boundValue);
+ break;
+ case gl.FRAMEBUFFER_BINDING:
+ gl.bindFramebuffer(gl.FRAMEBUFFER, boundValue);
+ break;
+ case gl.RENDERBUFFER_BINDING:
+ gl.bindRenderbuffer(gl.RENDERBUFFER, boundValue);
+ break;
+ case gl.TEXTURE_BINDING_2D:
+ var textureUnit = bindings[++i];
+ if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31)
+ break;
+ gl.activeTexture(textureUnit);
+ gl.bindTexture(gl.TEXTURE_2D, boundValue);
+ break;
+ case gl.TEXTURE_BINDING_CUBE_MAP:
+ var textureUnit = bindings[++i];
+ if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31)
+ break;
+ gl.activeTexture(textureUnit);
+ gl.bindTexture(gl.TEXTURE_CUBE_MAP, boundValue);
+ break;
+ case gl.VIEWPORT:
+ gl.viewport(boundValue[0], boundValue[1], boundValue[2], boundValue[3]);
+ break;
+ case gl.BLEND:
+ case gl.CULL_FACE:
+ case gl.DEPTH_TEST:
+ case gl.SCISSOR_TEST:
+ case gl.STENCIL_TEST:
+ if (boundValue) {
+ gl.enable(binding);
+ } else {
+ gl.disable(binding);
+ }
+ break;
+ default:
+ console.log("No GL restore behavior for 0x" + binding.toString(16));
+ break;
+ }
+ if (activeTexture) {
+ gl.activeTexture(activeTexture);
+ }
+ }
+ }
+ var glPreserveState = WGLUPreserveGLState;
+ var distortionVS = ['attribute vec2 position;', 'attribute vec3 texCoord;', 'varying vec2 vTexCoord;', 'uniform vec4 viewportOffsetScale[2];', 'void main() {', ' vec4 viewport = viewportOffsetScale[int(texCoord.z)];', ' vTexCoord = (texCoord.xy * viewport.zw) + viewport.xy;', ' gl_Position = vec4( position, 1.0, 1.0 );', '}'].join('\n');
+ var distortionFS = ['precision mediump float;', 'uniform sampler2D diffuse;', 'varying vec2 vTexCoord;', 'void main() {', ' gl_FragColor = texture2D(diffuse, vTexCoord);', '}'].join('\n');
+ function CardboardDistorter(gl, cardboardUI, bufferScale, dirtySubmitFrameBindings) {
+ this.gl = gl;
+ this.cardboardUI = cardboardUI;
+ this.bufferScale = bufferScale;
+ this.dirtySubmitFrameBindings = dirtySubmitFrameBindings;
+ this.ctxAttribs = gl.getContextAttributes();
+ this.meshWidth = 20;
+ this.meshHeight = 20;
+ this.bufferWidth = gl.drawingBufferWidth;
+ this.bufferHeight = gl.drawingBufferHeight;
+ this.realBindFramebuffer = gl.bindFramebuffer;
+ this.realEnable = gl.enable;
+ this.realDisable = gl.disable;
+ this.realColorMask = gl.colorMask;
+ this.realClearColor = gl.clearColor;
+ this.realViewport = gl.viewport;
+ if (!isIOS()) {
+ this.realCanvasWidth = Object.getOwnPropertyDescriptor(gl.canvas.__proto__, 'width');
+ this.realCanvasHeight = Object.getOwnPropertyDescriptor(gl.canvas.__proto__, 'height');
+ }
+ this.isPatched = false;
+ this.lastBoundFramebuffer = null;
+ this.cullFace = false;
+ this.depthTest = false;
+ this.blend = false;
+ this.scissorTest = false;
+ this.stencilTest = false;
+ this.viewport = [0, 0, 0, 0];
+ this.colorMask = [true, true, true, true];
+ this.clearColor = [0, 0, 0, 0];
+ this.attribs = {
+ position: 0,
+ texCoord: 1
+ };
+ this.program = linkProgram(gl, distortionVS, distortionFS, this.attribs);
+ this.uniforms = getProgramUniforms(gl, this.program);
+ this.viewportOffsetScale = new Float32Array(8);
+ this.setTextureBounds();
+ this.vertexBuffer = gl.createBuffer();
+ this.indexBuffer = gl.createBuffer();
+ this.indexCount = 0;
+ this.renderTarget = gl.createTexture();
+ this.framebuffer = gl.createFramebuffer();
+ this.depthStencilBuffer = null;
+ this.depthBuffer = null;
+ this.stencilBuffer = null;
+ if (this.ctxAttribs.depth && this.ctxAttribs.stencil) {
+ this.depthStencilBuffer = gl.createRenderbuffer();
+ } else if (this.ctxAttribs.depth) {
+ this.depthBuffer = gl.createRenderbuffer();
+ } else if (this.ctxAttribs.stencil) {
+ this.stencilBuffer = gl.createRenderbuffer();
+ }
+ this.patch();
+ this.onResize();
+ }
+ CardboardDistorter.prototype.destroy = function () {
+ var gl = this.gl;
+ this.unpatch();
+ gl.deleteProgram(this.program);
+ gl.deleteBuffer(this.vertexBuffer);
+ gl.deleteBuffer(this.indexBuffer);
+ gl.deleteTexture(this.renderTarget);
+ gl.deleteFramebuffer(this.framebuffer);
+ if (this.depthStencilBuffer) {
+ gl.deleteRenderbuffer(this.depthStencilBuffer);
+ }
+ if (this.depthBuffer) {
+ gl.deleteRenderbuffer(this.depthBuffer);
+ }
+ if (this.stencilBuffer) {
+ gl.deleteRenderbuffer(this.stencilBuffer);
+ }
+ if (this.cardboardUI) {
+ this.cardboardUI.destroy();
+ }
+ };
+ CardboardDistorter.prototype.onResize = function () {
+ var gl = this.gl;
+ var self = this;
+ var glState = [gl.RENDERBUFFER_BINDING, gl.TEXTURE_BINDING_2D, gl.TEXTURE0];
+ glPreserveState(gl, glState, function (gl) {
+ self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, null);
+ if (self.scissorTest) {
+ self.realDisable.call(gl, gl.SCISSOR_TEST);
+ }
+ self.realColorMask.call(gl, true, true, true, true);
+ self.realViewport.call(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
+ self.realClearColor.call(gl, 0, 0, 0, 1);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.framebuffer);
+ gl.bindTexture(gl.TEXTURE_2D, self.renderTarget);
+ gl.texImage2D(gl.TEXTURE_2D, 0, self.ctxAttribs.alpha ? gl.RGBA : gl.RGB, self.bufferWidth, self.bufferHeight, 0, self.ctxAttribs.alpha ? gl.RGBA : gl.RGB, gl.UNSIGNED_BYTE, null);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, self.renderTarget, 0);
+ if (self.ctxAttribs.depth && self.ctxAttribs.stencil) {
+ gl.bindRenderbuffer(gl.RENDERBUFFER, self.depthStencilBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, self.bufferWidth, self.bufferHeight);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, self.depthStencilBuffer);
+ } else if (self.ctxAttribs.depth) {
+ gl.bindRenderbuffer(gl.RENDERBUFFER, self.depthBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, self.bufferWidth, self.bufferHeight);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, self.depthBuffer);
+ } else if (self.ctxAttribs.stencil) {
+ gl.bindRenderbuffer(gl.RENDERBUFFER, self.stencilBuffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, self.bufferWidth, self.bufferHeight);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, self.stencilBuffer);
+ }
+ if (!gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) {
+ console.error('Framebuffer incomplete!');
+ }
+ self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.lastBoundFramebuffer);
+ if (self.scissorTest) {
+ self.realEnable.call(gl, gl.SCISSOR_TEST);
+ }
+ self.realColorMask.apply(gl, self.colorMask);
+ self.realViewport.apply(gl, self.viewport);
+ self.realClearColor.apply(gl, self.clearColor);
+ });
+ if (this.cardboardUI) {
+ this.cardboardUI.onResize();
+ }
+ };
+ CardboardDistorter.prototype.patch = function () {
+ if (this.isPatched) {
+ return;
+ }
+ var self = this;
+ var canvas = this.gl.canvas;
+ var gl = this.gl;
+ if (!isIOS()) {
+ canvas.width = getScreenWidth() * this.bufferScale;
+ canvas.height = getScreenHeight() * this.bufferScale;
+ Object.defineProperty(canvas, 'width', {
+ configurable: true,
+ enumerable: true,
+ get: function get() {
+ return self.bufferWidth;
+ },
+ set: function set(value) {
+ self.bufferWidth = value;
+ self.realCanvasWidth.set.call(canvas, value);
+ self.onResize();
+ }
+ });
+ Object.defineProperty(canvas, 'height', {
+ configurable: true,
+ enumerable: true,
+ get: function get() {
+ return self.bufferHeight;
+ },
+ set: function set(value) {
+ self.bufferHeight = value;
+ self.realCanvasHeight.set.call(canvas, value);
+ self.onResize();
+ }
+ });
+ }
+ this.lastBoundFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
+ if (this.lastBoundFramebuffer == null) {
+ this.lastBoundFramebuffer = this.framebuffer;
+ this.gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
+ }
+ this.gl.bindFramebuffer = function (target, framebuffer) {
+ self.lastBoundFramebuffer = framebuffer ? framebuffer : self.framebuffer;
+ self.realBindFramebuffer.call(gl, target, self.lastBoundFramebuffer);
+ };
+ this.cullFace = gl.getParameter(gl.CULL_FACE);
+ this.depthTest = gl.getParameter(gl.DEPTH_TEST);
+ this.blend = gl.getParameter(gl.BLEND);
+ this.scissorTest = gl.getParameter(gl.SCISSOR_TEST);
+ this.stencilTest = gl.getParameter(gl.STENCIL_TEST);
+ gl.enable = function (pname) {
+ switch (pname) {
+ case gl.CULL_FACE:
+ self.cullFace = true;break;
+ case gl.DEPTH_TEST:
+ self.depthTest = true;break;
+ case gl.BLEND:
+ self.blend = true;break;
+ case gl.SCISSOR_TEST:
+ self.scissorTest = true;break;
+ case gl.STENCIL_TEST:
+ self.stencilTest = true;break;
+ }
+ self.realEnable.call(gl, pname);
+ };
+ gl.disable = function (pname) {
+ switch (pname) {
+ case gl.CULL_FACE:
+ self.cullFace = false;break;
+ case gl.DEPTH_TEST:
+ self.depthTest = false;break;
+ case gl.BLEND:
+ self.blend = false;break;
+ case gl.SCISSOR_TEST:
+ self.scissorTest = false;break;
+ case gl.STENCIL_TEST:
+ self.stencilTest = false;break;
+ }
+ self.realDisable.call(gl, pname);
+ };
+ this.colorMask = gl.getParameter(gl.COLOR_WRITEMASK);
+ gl.colorMask = function (r, g, b, a) {
+ self.colorMask[0] = r;
+ self.colorMask[1] = g;
+ self.colorMask[2] = b;
+ self.colorMask[3] = a;
+ self.realColorMask.call(gl, r, g, b, a);
+ };
+ this.clearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE);
+ gl.clearColor = function (r, g, b, a) {
+ self.clearColor[0] = r;
+ self.clearColor[1] = g;
+ self.clearColor[2] = b;
+ self.clearColor[3] = a;
+ self.realClearColor.call(gl, r, g, b, a);
+ };
+ this.viewport = gl.getParameter(gl.VIEWPORT);
+ gl.viewport = function (x, y, w, h) {
+ self.viewport[0] = x;
+ self.viewport[1] = y;
+ self.viewport[2] = w;
+ self.viewport[3] = h;
+ self.realViewport.call(gl, x, y, w, h);
+ };
+ this.isPatched = true;
+ safariCssSizeWorkaround(canvas);
+ };
+ CardboardDistorter.prototype.unpatch = function () {
+ if (!this.isPatched) {
+ return;
+ }
+ var gl = this.gl;
+ var canvas = this.gl.canvas;
+ if (!isIOS()) {
+ Object.defineProperty(canvas, 'width', this.realCanvasWidth);
+ Object.defineProperty(canvas, 'height', this.realCanvasHeight);
+ }
+ canvas.width = this.bufferWidth;
+ canvas.height = this.bufferHeight;
+ gl.bindFramebuffer = this.realBindFramebuffer;
+ gl.enable = this.realEnable;
+ gl.disable = this.realDisable;
+ gl.colorMask = this.realColorMask;
+ gl.clearColor = this.realClearColor;
+ gl.viewport = this.realViewport;
+ if (this.lastBoundFramebuffer == this.framebuffer) {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ }
+ this.isPatched = false;
+ setTimeout(function () {
+ safariCssSizeWorkaround(canvas);
+ }, 1);
+ };
+ CardboardDistorter.prototype.setTextureBounds = function (leftBounds, rightBounds) {
+ if (!leftBounds) {
+ leftBounds = [0, 0, 0.5, 1];
+ }
+ if (!rightBounds) {
+ rightBounds = [0.5, 0, 0.5, 1];
+ }
+ this.viewportOffsetScale[0] = leftBounds[0];
+ this.viewportOffsetScale[1] = leftBounds[1];
+ this.viewportOffsetScale[2] = leftBounds[2];
+ this.viewportOffsetScale[3] = leftBounds[3];
+ this.viewportOffsetScale[4] = rightBounds[0];
+ this.viewportOffsetScale[5] = rightBounds[1];
+ this.viewportOffsetScale[6] = rightBounds[2];
+ this.viewportOffsetScale[7] = rightBounds[3];
+ };
+ CardboardDistorter.prototype.submitFrame = function () {
+ var gl = this.gl;
+ var self = this;
+ var glState = [];
+ if (!this.dirtySubmitFrameBindings) {
+ glState.push(gl.CURRENT_PROGRAM, gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING, gl.TEXTURE_BINDING_2D, gl.TEXTURE0);
+ }
+ glPreserveState(gl, glState, function (gl) {
+ self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, null);
+ if (self.cullFace) {
+ self.realDisable.call(gl, gl.CULL_FACE);
+ }
+ if (self.depthTest) {
+ self.realDisable.call(gl, gl.DEPTH_TEST);
+ }
+ if (self.blend) {
+ self.realDisable.call(gl, gl.BLEND);
+ }
+ if (self.scissorTest) {
+ self.realDisable.call(gl, gl.SCISSOR_TEST);
+ }
+ if (self.stencilTest) {
+ self.realDisable.call(gl, gl.STENCIL_TEST);
+ }
+ self.realColorMask.call(gl, true, true, true, true);
+ self.realViewport.call(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
+ if (self.ctxAttribs.alpha || isIOS()) {
+ self.realClearColor.call(gl, 0, 0, 0, 1);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ }
+ gl.useProgram(self.program);
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.indexBuffer);
+ gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer);
+ gl.enableVertexAttribArray(self.attribs.position);
+ gl.enableVertexAttribArray(self.attribs.texCoord);
+ gl.vertexAttribPointer(self.attribs.position, 2, gl.FLOAT, false, 20, 0);
+ gl.vertexAttribPointer(self.attribs.texCoord, 3, gl.FLOAT, false, 20, 8);
+ gl.activeTexture(gl.TEXTURE0);
+ gl.uniform1i(self.uniforms.diffuse, 0);
+ gl.bindTexture(gl.TEXTURE_2D, self.renderTarget);
+ gl.uniform4fv(self.uniforms.viewportOffsetScale, self.viewportOffsetScale);
+ gl.drawElements(gl.TRIANGLES, self.indexCount, gl.UNSIGNED_SHORT, 0);
+ if (self.cardboardUI) {
+ self.cardboardUI.renderNoState();
+ }
+ self.realBindFramebuffer.call(self.gl, gl.FRAMEBUFFER, self.framebuffer);
+ if (!self.ctxAttribs.preserveDrawingBuffer) {
+ self.realClearColor.call(gl, 0, 0, 0, 0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ }
+ if (!self.dirtySubmitFrameBindings) {
+ self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.lastBoundFramebuffer);
+ }
+ if (self.cullFace) {
+ self.realEnable.call(gl, gl.CULL_FACE);
+ }
+ if (self.depthTest) {
+ self.realEnable.call(gl, gl.DEPTH_TEST);
+ }
+ if (self.blend) {
+ self.realEnable.call(gl, gl.BLEND);
+ }
+ if (self.scissorTest) {
+ self.realEnable.call(gl, gl.SCISSOR_TEST);
+ }
+ if (self.stencilTest) {
+ self.realEnable.call(gl, gl.STENCIL_TEST);
+ }
+ self.realColorMask.apply(gl, self.colorMask);
+ self.realViewport.apply(gl, self.viewport);
+ if (self.ctxAttribs.alpha || !self.ctxAttribs.preserveDrawingBuffer) {
+ self.realClearColor.apply(gl, self.clearColor);
+ }
+ });
+ if (isIOS()) {
+ var canvas = gl.canvas;
+ if (canvas.width != self.bufferWidth || canvas.height != self.bufferHeight) {
+ self.bufferWidth = canvas.width;
+ self.bufferHeight = canvas.height;
+ self.onResize();
+ }
+ }
+ };
+ CardboardDistorter.prototype.updateDeviceInfo = function (deviceInfo) {
+ var gl = this.gl;
+ var self = this;
+ var glState = [gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING];
+ glPreserveState(gl, glState, function (gl) {
+ var vertices = self.computeMeshVertices_(self.meshWidth, self.meshHeight, deviceInfo);
+ gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
+ if (!self.indexCount) {
+ var indices = self.computeMeshIndices_(self.meshWidth, self.meshHeight);
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.indexBuffer);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
+ self.indexCount = indices.length;
+ }
+ });
+ };
+ CardboardDistorter.prototype.computeMeshVertices_ = function (width, height, deviceInfo) {
+ var vertices = new Float32Array(2 * width * height * 5);
+ var lensFrustum = deviceInfo.getLeftEyeVisibleTanAngles();
+ var noLensFrustum = deviceInfo.getLeftEyeNoLensTanAngles();
+ var viewport = deviceInfo.getLeftEyeVisibleScreenRect(noLensFrustum);
+ var vidx = 0;
+ for (var e = 0; e < 2; e++) {
+ for (var j = 0; j < height; j++) {
+ for (var i = 0; i < width; i++, vidx++) {
+ var u = i / (width - 1);
+ var v = j / (height - 1);
+ var s = u;
+ var t = v;
+ var x = lerp(lensFrustum[0], lensFrustum[2], u);
+ var y = lerp(lensFrustum[3], lensFrustum[1], v);
+ var d = Math.sqrt(x * x + y * y);
+ var r = deviceInfo.distortion.distortInverse(d);
+ var p = x * r / d;
+ var q = y * r / d;
+ u = (p - noLensFrustum[0]) / (noLensFrustum[2] - noLensFrustum[0]);
+ v = (q - noLensFrustum[3]) / (noLensFrustum[1] - noLensFrustum[3]);
+ u = (viewport.x + u * viewport.width - 0.5) * 2.0;
+ v = (viewport.y + v * viewport.height - 0.5) * 2.0;
+ vertices[vidx * 5 + 0] = u;
+ vertices[vidx * 5 + 1] = v;
+ vertices[vidx * 5 + 2] = s;
+ vertices[vidx * 5 + 3] = t;
+ vertices[vidx * 5 + 4] = e;
+ }
+ }
+ var w = lensFrustum[2] - lensFrustum[0];
+ lensFrustum[0] = -(w + lensFrustum[0]);
+ lensFrustum[2] = w - lensFrustum[2];
+ w = noLensFrustum[2] - noLensFrustum[0];
+ noLensFrustum[0] = -(w + noLensFrustum[0]);
+ noLensFrustum[2] = w - noLensFrustum[2];
+ viewport.x = 1 - (viewport.x + viewport.width);
+ }
+ return vertices;
+ };
+ CardboardDistorter.prototype.computeMeshIndices_ = function (width, height) {
+ var indices = new Uint16Array(2 * (width - 1) * (height - 1) * 6);
+ var halfwidth = width / 2;
+ var halfheight = height / 2;
+ var vidx = 0;
+ var iidx = 0;
+ for (var e = 0; e < 2; e++) {
+ for (var j = 0; j < height; j++) {
+ for (var i = 0; i < width; i++, vidx++) {
+ if (i == 0 || j == 0) continue;
+ if (i <= halfwidth == j <= halfheight) {
+ indices[iidx++] = vidx;
+ indices[iidx++] = vidx - width - 1;
+ indices[iidx++] = vidx - width;
+ indices[iidx++] = vidx - width - 1;
+ indices[iidx++] = vidx;
+ indices[iidx++] = vidx - 1;
+ } else {
+ indices[iidx++] = vidx - 1;
+ indices[iidx++] = vidx - width;
+ indices[iidx++] = vidx;
+ indices[iidx++] = vidx - width;
+ indices[iidx++] = vidx - 1;
+ indices[iidx++] = vidx - width - 1;
+ }
+ }
+ }
+ }
+ return indices;
+ };
+ CardboardDistorter.prototype.getOwnPropertyDescriptor_ = function (proto, attrName) {
+ var descriptor = Object.getOwnPropertyDescriptor(proto, attrName);
+ if (descriptor.get === undefined || descriptor.set === undefined) {
+ descriptor.configurable = true;
+ descriptor.enumerable = true;
+ descriptor.get = function () {
+ return this.getAttribute(attrName);
+ };
+ descriptor.set = function (val) {
+ this.setAttribute(attrName, val);
+ };
+ }
+ return descriptor;
+ };
+ var uiVS = ['attribute vec2 position;', 'uniform mat4 projectionMat;', 'void main() {', ' gl_Position = projectionMat * vec4( position, -1.0, 1.0 );', '}'].join('\n');
+ var uiFS = ['precision mediump float;', 'uniform vec4 color;', 'void main() {', ' gl_FragColor = color;', '}'].join('\n');
+ var DEG2RAD = Math.PI / 180.0;
+ var kAnglePerGearSection = 60;
+ var kOuterRimEndAngle = 12;
+ var kInnerRimBeginAngle = 20;
+ var kOuterRadius = 1;
+ var kMiddleRadius = 0.75;
+ var kInnerRadius = 0.3125;
+ var kCenterLineThicknessDp = 4;
+ var kButtonWidthDp = 28;
+ var kTouchSlopFactor = 1.5;
+ function CardboardUI(gl) {
+ this.gl = gl;
+ this.attribs = {
+ position: 0
+ };
+ this.program = linkProgram(gl, uiVS, uiFS, this.attribs);
+ this.uniforms = getProgramUniforms(gl, this.program);
+ this.vertexBuffer = gl.createBuffer();
+ this.gearOffset = 0;
+ this.gearVertexCount = 0;
+ this.arrowOffset = 0;
+ this.arrowVertexCount = 0;
+ this.projMat = new Float32Array(16);
+ this.listener = null;
+ this.onResize();
+ }
+ CardboardUI.prototype.destroy = function () {
+ var gl = this.gl;
+ if (this.listener) {
+ gl.canvas.removeEventListener('click', this.listener, false);
+ }
+ gl.deleteProgram(this.program);
+ gl.deleteBuffer(this.vertexBuffer);
+ };
+ CardboardUI.prototype.listen = function (optionsCallback, backCallback) {
+ var canvas = this.gl.canvas;
+ this.listener = function (event) {
+ var midline = canvas.clientWidth / 2;
+ var buttonSize = kButtonWidthDp * kTouchSlopFactor;
+ if (event.clientX > midline - buttonSize && event.clientX < midline + buttonSize && event.clientY > canvas.clientHeight - buttonSize) {
+ optionsCallback(event);
+ }
+ else if (event.clientX < buttonSize && event.clientY < buttonSize) {
+ backCallback(event);
+ }
+ };
+ canvas.addEventListener('click', this.listener, false);
+ };
+ CardboardUI.prototype.onResize = function () {
+ var gl = this.gl;
+ var self = this;
+ var glState = [gl.ARRAY_BUFFER_BINDING];
+ glPreserveState(gl, glState, function (gl) {
+ var vertices = [];
+ var midline = gl.drawingBufferWidth / 2;
+ var physicalPixels = Math.max(screen.width, screen.height) * window.devicePixelRatio;
+ var scalingRatio = gl.drawingBufferWidth / physicalPixels;
+ var dps = scalingRatio * window.devicePixelRatio;
+ var lineWidth = kCenterLineThicknessDp * dps / 2;
+ var buttonSize = kButtonWidthDp * kTouchSlopFactor * dps;
+ var buttonScale = kButtonWidthDp * dps / 2;
+ var buttonBorder = (kButtonWidthDp * kTouchSlopFactor - kButtonWidthDp) * dps;
+ vertices.push(midline - lineWidth, buttonSize);
+ vertices.push(midline - lineWidth, gl.drawingBufferHeight);
+ vertices.push(midline + lineWidth, buttonSize);
+ vertices.push(midline + lineWidth, gl.drawingBufferHeight);
+ self.gearOffset = vertices.length / 2;
+ function addGearSegment(theta, r) {
+ var angle = (90 - theta) * DEG2RAD;
+ var x = Math.cos(angle);
+ var y = Math.sin(angle);
+ vertices.push(kInnerRadius * x * buttonScale + midline, kInnerRadius * y * buttonScale + buttonScale);
+ vertices.push(r * x * buttonScale + midline, r * y * buttonScale + buttonScale);
+ }
+ for (var i = 0; i <= 6; i++) {
+ var segmentTheta = i * kAnglePerGearSection;
+ addGearSegment(segmentTheta, kOuterRadius);
+ addGearSegment(segmentTheta + kOuterRimEndAngle, kOuterRadius);
+ addGearSegment(segmentTheta + kInnerRimBeginAngle, kMiddleRadius);
+ addGearSegment(segmentTheta + (kAnglePerGearSection - kInnerRimBeginAngle), kMiddleRadius);
+ addGearSegment(segmentTheta + (kAnglePerGearSection - kOuterRimEndAngle), kOuterRadius);
+ }
+ self.gearVertexCount = vertices.length / 2 - self.gearOffset;
+ self.arrowOffset = vertices.length / 2;
+ function addArrowVertex(x, y) {
+ vertices.push(buttonBorder + x, gl.drawingBufferHeight - buttonBorder - y);
+ }
+ var angledLineWidth = lineWidth / Math.sin(45 * DEG2RAD);
+ addArrowVertex(0, buttonScale);
+ addArrowVertex(buttonScale, 0);
+ addArrowVertex(buttonScale + angledLineWidth, angledLineWidth);
+ addArrowVertex(angledLineWidth, buttonScale + angledLineWidth);
+ addArrowVertex(angledLineWidth, buttonScale - angledLineWidth);
+ addArrowVertex(0, buttonScale);
+ addArrowVertex(buttonScale, buttonScale * 2);
+ addArrowVertex(buttonScale + angledLineWidth, buttonScale * 2 - angledLineWidth);
+ addArrowVertex(angledLineWidth, buttonScale - angledLineWidth);
+ addArrowVertex(0, buttonScale);
+ addArrowVertex(angledLineWidth, buttonScale - lineWidth);
+ addArrowVertex(kButtonWidthDp * dps, buttonScale - lineWidth);
+ addArrowVertex(angledLineWidth, buttonScale + lineWidth);
+ addArrowVertex(kButtonWidthDp * dps, buttonScale + lineWidth);
+ self.arrowVertexCount = vertices.length / 2 - self.arrowOffset;
+ gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
+ });
+ };
+ CardboardUI.prototype.render = function () {
+ var gl = this.gl;
+ var self = this;
+ var glState = [gl.CULL_FACE, gl.DEPTH_TEST, gl.BLEND, gl.SCISSOR_TEST, gl.STENCIL_TEST, gl.COLOR_WRITEMASK, gl.VIEWPORT, gl.CURRENT_PROGRAM, gl.ARRAY_BUFFER_BINDING];
+ glPreserveState(gl, glState, function (gl) {
+ gl.disable(gl.CULL_FACE);
+ gl.disable(gl.DEPTH_TEST);
+ gl.disable(gl.BLEND);
+ gl.disable(gl.SCISSOR_TEST);
+ gl.disable(gl.STENCIL_TEST);
+ gl.colorMask(true, true, true, true);
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
+ self.renderNoState();
+ });
+ };
+ CardboardUI.prototype.renderNoState = function () {
+ var gl = this.gl;
+ gl.useProgram(this.program);
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
+ gl.enableVertexAttribArray(this.attribs.position);
+ gl.vertexAttribPointer(this.attribs.position, 2, gl.FLOAT, false, 8, 0);
+ gl.uniform4f(this.uniforms.color, 1.0, 1.0, 1.0, 1.0);
+ orthoMatrix(this.projMat, 0, gl.drawingBufferWidth, 0, gl.drawingBufferHeight, 0.1, 1024.0);
+ gl.uniformMatrix4fv(this.uniforms.projectionMat, false, this.projMat);
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+ gl.drawArrays(gl.TRIANGLE_STRIP, this.gearOffset, this.gearVertexCount);
+ gl.drawArrays(gl.TRIANGLE_STRIP, this.arrowOffset, this.arrowVertexCount);
+ };
+ function Distortion(coefficients) {
+ this.coefficients = coefficients;
+ }
+ Distortion.prototype.distortInverse = function (radius) {
+ var r0 = 0;
+ var r1 = 1;
+ var dr0 = radius - this.distort(r0);
+ while (Math.abs(r1 - r0) > 0.0001 ) {
+ var dr1 = radius - this.distort(r1);
+ var r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0));
+ r0 = r1;
+ r1 = r2;
+ dr0 = dr1;
+ }
+ return r1;
+ };
+ Distortion.prototype.distort = function (radius) {
+ var r2 = radius * radius;
+ var ret = 0;
+ for (var i = 0; i < this.coefficients.length; i++) {
+ ret = r2 * (ret + this.coefficients[i]);
+ }
+ return (ret + 1) * radius;
+ };
+ var degToRad = Math.PI / 180;
+ var radToDeg = 180 / Math.PI;
+ var Vector3 = function Vector3(x, y, z) {
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+ };
+ Vector3.prototype = {
+ constructor: Vector3,
+ set: function set(x, y, z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ return this;
+ },
+ copy: function copy(v) {
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+ return this;
+ },
+ length: function length() {
+ return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
+ },
+ normalize: function normalize() {
+ var scalar = this.length();
+ if (scalar !== 0) {
+ var invScalar = 1 / scalar;
+ this.multiplyScalar(invScalar);
+ } else {
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ }
+ return this;
+ },
+ multiplyScalar: function multiplyScalar(scalar) {
+ this.x *= scalar;
+ this.y *= scalar;
+ this.z *= scalar;
+ },
+ applyQuaternion: function applyQuaternion(q) {
+ var x = this.x;
+ var y = this.y;
+ var z = this.z;
+ var qx = q.x;
+ var qy = q.y;
+ var qz = q.z;
+ var qw = q.w;
+ var ix = qw * x + qy * z - qz * y;
+ var iy = qw * y + qz * x - qx * z;
+ var iz = qw * z + qx * y - qy * x;
+ var iw = -qx * x - qy * y - qz * z;
+ this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
+ this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
+ this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
+ return this;
+ },
+ dot: function dot(v) {
+ return this.x * v.x + this.y * v.y + this.z * v.z;
+ },
+ crossVectors: function crossVectors(a, b) {
+ var ax = a.x,
+ ay = a.y,
+ az = a.z;
+ var bx = b.x,
+ by = b.y,
+ bz = b.z;
+ this.x = ay * bz - az * by;
+ this.y = az * bx - ax * bz;
+ this.z = ax * by - ay * bx;
+ return this;
+ }
+ };
+ var Quaternion = function Quaternion(x, y, z, w) {
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+ this.w = w !== undefined ? w : 1;
+ };
+ Quaternion.prototype = {
+ constructor: Quaternion,
+ set: function set(x, y, z, w) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ return this;
+ },
+ copy: function copy(quaternion) {
+ this.x = quaternion.x;
+ this.y = quaternion.y;
+ this.z = quaternion.z;
+ this.w = quaternion.w;
+ return this;
+ },
+ setFromEulerXYZ: function setFromEulerXYZ(x, y, z) {
+ var c1 = Math.cos(x / 2);
+ var c2 = Math.cos(y / 2);
+ var c3 = Math.cos(z / 2);
+ var s1 = Math.sin(x / 2);
+ var s2 = Math.sin(y / 2);
+ var s3 = Math.sin(z / 2);
+ this.x = s1 * c2 * c3 + c1 * s2 * s3;
+ this.y = c1 * s2 * c3 - s1 * c2 * s3;
+ this.z = c1 * c2 * s3 + s1 * s2 * c3;
+ this.w = c1 * c2 * c3 - s1 * s2 * s3;
+ return this;
+ },
+ setFromEulerYXZ: function setFromEulerYXZ(x, y, z) {
+ var c1 = Math.cos(x / 2);
+ var c2 = Math.cos(y / 2);
+ var c3 = Math.cos(z / 2);
+ var s1 = Math.sin(x / 2);
+ var s2 = Math.sin(y / 2);
+ var s3 = Math.sin(z / 2);
+ this.x = s1 * c2 * c3 + c1 * s2 * s3;
+ this.y = c1 * s2 * c3 - s1 * c2 * s3;
+ this.z = c1 * c2 * s3 - s1 * s2 * c3;
+ this.w = c1 * c2 * c3 + s1 * s2 * s3;
+ return this;
+ },
+ setFromAxisAngle: function setFromAxisAngle(axis, angle) {
+ var halfAngle = angle / 2,
+ s = Math.sin(halfAngle);
+ this.x = axis.x * s;
+ this.y = axis.y * s;
+ this.z = axis.z * s;
+ this.w = Math.cos(halfAngle);
+ return this;
+ },
+ multiply: function multiply(q) {
+ return this.multiplyQuaternions(this, q);
+ },
+ multiplyQuaternions: function multiplyQuaternions(a, b) {
+ var qax = a.x,
+ qay = a.y,
+ qaz = a.z,
+ qaw = a.w;
+ var qbx = b.x,
+ qby = b.y,
+ qbz = b.z,
+ qbw = b.w;
+ this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
+ this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
+ this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
+ this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
+ return this;
+ },
+ inverse: function inverse() {
+ this.x *= -1;
+ this.y *= -1;
+ this.z *= -1;
+ this.normalize();
+ return this;
+ },
+ normalize: function normalize() {
+ var l = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
+ if (l === 0) {
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ this.w = 1;
+ } else {
+ l = 1 / l;
+ this.x = this.x * l;
+ this.y = this.y * l;
+ this.z = this.z * l;
+ this.w = this.w * l;
+ }
+ return this;
+ },
+ slerp: function slerp(qb, t) {
+ if (t === 0) return this;
+ if (t === 1) return this.copy(qb);
+ var x = this.x,
+ y = this.y,
+ z = this.z,
+ w = this.w;
+ var cosHalfTheta = w * qb.w + x * qb.x + y * qb.y + z * qb.z;
+ if (cosHalfTheta < 0) {
+ this.w = -qb.w;
+ this.x = -qb.x;
+ this.y = -qb.y;
+ this.z = -qb.z;
+ cosHalfTheta = -cosHalfTheta;
+ } else {
+ this.copy(qb);
+ }
+ if (cosHalfTheta >= 1.0) {
+ this.w = w;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ return this;
+ }
+ var halfTheta = Math.acos(cosHalfTheta);
+ var sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta);
+ if (Math.abs(sinHalfTheta) < 0.001) {
+ this.w = 0.5 * (w + this.w);
+ this.x = 0.5 * (x + this.x);
+ this.y = 0.5 * (y + this.y);
+ this.z = 0.5 * (z + this.z);
+ return this;
+ }
+ var ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta,
+ ratioB = Math.sin(t * halfTheta) / sinHalfTheta;
+ this.w = w * ratioA + this.w * ratioB;
+ this.x = x * ratioA + this.x * ratioB;
+ this.y = y * ratioA + this.y * ratioB;
+ this.z = z * ratioA + this.z * ratioB;
+ return this;
+ },
+ setFromUnitVectors: function () {
+ var v1, r;
+ var EPS = 0.000001;
+ return function (vFrom, vTo) {
+ if (v1 === undefined) v1 = new Vector3();
+ r = vFrom.dot(vTo) + 1;
+ if (r < EPS) {
+ r = 0;
+ if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) {
+ v1.set(-vFrom.y, vFrom.x, 0);
+ } else {
+ v1.set(0, -vFrom.z, vFrom.y);
+ }
+ } else {
+ v1.crossVectors(vFrom, vTo);
+ }
+ this.x = v1.x;
+ this.y = v1.y;
+ this.z = v1.z;
+ this.w = r;
+ this.normalize();
+ return this;
+ };
+ }()
+ };
+ function Device(params) {
+ this.width = params.width || getScreenWidth();
+ this.height = params.height || getScreenHeight();
+ this.widthMeters = params.widthMeters;
+ this.heightMeters = params.heightMeters;
+ this.bevelMeters = params.bevelMeters;
+ }
+ var DEFAULT_ANDROID = new Device({
+ widthMeters: 0.110,
+ heightMeters: 0.062,
+ bevelMeters: 0.004
+ });
+ var DEFAULT_IOS = new Device({
+ widthMeters: 0.1038,
+ heightMeters: 0.0584,
+ bevelMeters: 0.004
+ });
+ var Viewers = {
+ CardboardV1: new CardboardViewer({
+ id: 'CardboardV1',
+ label: 'Cardboard I/O 2014',
+ fov: 40,
+ interLensDistance: 0.060,
+ baselineLensDistance: 0.035,
+ screenLensDistance: 0.042,
+ distortionCoefficients: [0.441, 0.156],
+ inverseCoefficients: [-0.4410035, 0.42756155, -0.4804439, 0.5460139, -0.58821183, 0.5733938, -0.48303202, 0.33299083, -0.17573841, 0.0651772, -0.01488963, 0.001559834]
+ }),
+ CardboardV2: new CardboardViewer({
+ id: 'CardboardV2',
+ label: 'Cardboard I/O 2015',
+ fov: 60,
+ interLensDistance: 0.064,
+ baselineLensDistance: 0.035,
+ screenLensDistance: 0.039,
+ distortionCoefficients: [0.34, 0.55],
+ inverseCoefficients: [-0.33836704, -0.18162185, 0.862655, -1.2462051, 1.0560602, -0.58208317, 0.21609078, -0.05444823, 0.009177956, -9.904169E-4, 6.183535E-5, -1.6981803E-6]
+ })
+ };
+ function DeviceInfo(deviceParams, additionalViewers) {
+ this.viewer = Viewers.CardboardV2;
+ this.updateDeviceParams(deviceParams);
+ this.distortion = new Distortion(this.viewer.distortionCoefficients);
+ for (var i = 0; i < additionalViewers.length; i++) {
+ var viewer = additionalViewers[i];
+ Viewers[viewer.id] = new CardboardViewer(viewer);
+ }
+ }
+ DeviceInfo.prototype.updateDeviceParams = function (deviceParams) {
+ this.device = this.determineDevice_(deviceParams) || this.device;
+ };
+ DeviceInfo.prototype.getDevice = function () {
+ return this.device;
+ };
+ DeviceInfo.prototype.setViewer = function (viewer) {
+ this.viewer = viewer;
+ this.distortion = new Distortion(this.viewer.distortionCoefficients);
+ };
+ DeviceInfo.prototype.determineDevice_ = function (deviceParams) {
+ if (!deviceParams) {
+ if (isIOS()) {
+ console.warn('Using fallback iOS device measurements.');
+ return DEFAULT_IOS;
+ } else {
+ console.warn('Using fallback Android device measurements.');
+ return DEFAULT_ANDROID;
+ }
+ }
+ var METERS_PER_INCH = 0.0254;
+ var metersPerPixelX = METERS_PER_INCH / deviceParams.xdpi;
+ var metersPerPixelY = METERS_PER_INCH / deviceParams.ydpi;
+ var width = getScreenWidth();
+ var height = getScreenHeight();
+ return new Device({
+ widthMeters: metersPerPixelX * width,
+ heightMeters: metersPerPixelY * height,
+ bevelMeters: deviceParams.bevelMm * 0.001
+ });
+ };
+ DeviceInfo.prototype.getDistortedFieldOfViewLeftEye = function () {
+ var viewer = this.viewer;
+ var device = this.device;
+ var distortion = this.distortion;
+ var eyeToScreenDistance = viewer.screenLensDistance;
+ var outerDist = (device.widthMeters - viewer.interLensDistance) / 2;
+ var innerDist = viewer.interLensDistance / 2;
+ var bottomDist = viewer.baselineLensDistance - device.bevelMeters;
+ var topDist = device.heightMeters - bottomDist;
+ var outerAngle = radToDeg * Math.atan(distortion.distort(outerDist / eyeToScreenDistance));
+ var innerAngle = radToDeg * Math.atan(distortion.distort(innerDist / eyeToScreenDistance));
+ var bottomAngle = radToDeg * Math.atan(distortion.distort(bottomDist / eyeToScreenDistance));
+ var topAngle = radToDeg * Math.atan(distortion.distort(topDist / eyeToScreenDistance));
+ return {
+ leftDegrees: Math.min(outerAngle, viewer.fov),
+ rightDegrees: Math.min(innerAngle, viewer.fov),
+ downDegrees: Math.min(bottomAngle, viewer.fov),
+ upDegrees: Math.min(topAngle, viewer.fov)
+ };
+ };
+ DeviceInfo.prototype.getLeftEyeVisibleTanAngles = function () {
+ var viewer = this.viewer;
+ var device = this.device;
+ var distortion = this.distortion;
+ var fovLeft = Math.tan(-degToRad * viewer.fov);
+ var fovTop = Math.tan(degToRad * viewer.fov);
+ var fovRight = Math.tan(degToRad * viewer.fov);
+ var fovBottom = Math.tan(-degToRad * viewer.fov);
+ var halfWidth = device.widthMeters / 4;
+ var halfHeight = device.heightMeters / 2;
+ var verticalLensOffset = viewer.baselineLensDistance - device.bevelMeters - halfHeight;
+ var centerX = viewer.interLensDistance / 2 - halfWidth;
+ var centerY = -verticalLensOffset;
+ var centerZ = viewer.screenLensDistance;
+ var screenLeft = distortion.distort((centerX - halfWidth) / centerZ);
+ var screenTop = distortion.distort((centerY + halfHeight) / centerZ);
+ var screenRight = distortion.distort((centerX + halfWidth) / centerZ);
+ var screenBottom = distortion.distort((centerY - halfHeight) / centerZ);
+ var result = new Float32Array(4);
+ result[0] = Math.max(fovLeft, screenLeft);
+ result[1] = Math.min(fovTop, screenTop);
+ result[2] = Math.min(fovRight, screenRight);
+ result[3] = Math.max(fovBottom, screenBottom);
+ return result;
+ };
+ DeviceInfo.prototype.getLeftEyeNoLensTanAngles = function () {
+ var viewer = this.viewer;
+ var device = this.device;
+ var distortion = this.distortion;
+ var result = new Float32Array(4);
+ var fovLeft = distortion.distortInverse(Math.tan(-degToRad * viewer.fov));
+ var fovTop = distortion.distortInverse(Math.tan(degToRad * viewer.fov));
+ var fovRight = distortion.distortInverse(Math.tan(degToRad * viewer.fov));
+ var fovBottom = distortion.distortInverse(Math.tan(-degToRad * viewer.fov));
+ var halfWidth = device.widthMeters / 4;
+ var halfHeight = device.heightMeters / 2;
+ var verticalLensOffset = viewer.baselineLensDistance - device.bevelMeters - halfHeight;
+ var centerX = viewer.interLensDistance / 2 - halfWidth;
+ var centerY = -verticalLensOffset;
+ var centerZ = viewer.screenLensDistance;
+ var screenLeft = (centerX - halfWidth) / centerZ;
+ var screenTop = (centerY + halfHeight) / centerZ;
+ var screenRight = (centerX + halfWidth) / centerZ;
+ var screenBottom = (centerY - halfHeight) / centerZ;
+ result[0] = Math.max(fovLeft, screenLeft);
+ result[1] = Math.min(fovTop, screenTop);
+ result[2] = Math.min(fovRight, screenRight);
+ result[3] = Math.max(fovBottom, screenBottom);
+ return result;
+ };
+ DeviceInfo.prototype.getLeftEyeVisibleScreenRect = function (undistortedFrustum) {
+ var viewer = this.viewer;
+ var device = this.device;
+ var dist = viewer.screenLensDistance;
+ var eyeX = (device.widthMeters - viewer.interLensDistance) / 2;
+ var eyeY = viewer.baselineLensDistance - device.bevelMeters;
+ var left = (undistortedFrustum[0] * dist + eyeX) / device.widthMeters;
+ var top = (undistortedFrustum[1] * dist + eyeY) / device.heightMeters;
+ var right = (undistortedFrustum[2] * dist + eyeX) / device.widthMeters;
+ var bottom = (undistortedFrustum[3] * dist + eyeY) / device.heightMeters;
+ return {
+ x: left,
+ y: bottom,
+ width: right - left,
+ height: top - bottom
+ };
+ };
+ DeviceInfo.prototype.getFieldOfViewLeftEye = function (opt_isUndistorted) {
+ return opt_isUndistorted ? this.getUndistortedFieldOfViewLeftEye() : this.getDistortedFieldOfViewLeftEye();
+ };
+ DeviceInfo.prototype.getFieldOfViewRightEye = function (opt_isUndistorted) {
+ var fov = this.getFieldOfViewLeftEye(opt_isUndistorted);
+ return {
+ leftDegrees: fov.rightDegrees,
+ rightDegrees: fov.leftDegrees,
+ upDegrees: fov.upDegrees,
+ downDegrees: fov.downDegrees
+ };
+ };
+ DeviceInfo.prototype.getUndistortedFieldOfViewLeftEye = function () {
+ var p = this.getUndistortedParams_();
+ return {
+ leftDegrees: radToDeg * Math.atan(p.outerDist),
+ rightDegrees: radToDeg * Math.atan(p.innerDist),
+ downDegrees: radToDeg * Math.atan(p.bottomDist),
+ upDegrees: radToDeg * Math.atan(p.topDist)
+ };
+ };
+ DeviceInfo.prototype.getUndistortedViewportLeftEye = function () {
+ var p = this.getUndistortedParams_();
+ var viewer = this.viewer;
+ var device = this.device;
+ var eyeToScreenDistance = viewer.screenLensDistance;
+ var screenWidth = device.widthMeters / eyeToScreenDistance;
+ var screenHeight = device.heightMeters / eyeToScreenDistance;
+ var xPxPerTanAngle = device.width / screenWidth;
+ var yPxPerTanAngle = device.height / screenHeight;
+ var x = Math.round((p.eyePosX - p.outerDist) * xPxPerTanAngle);
+ var y = Math.round((p.eyePosY - p.bottomDist) * yPxPerTanAngle);
+ return {
+ x: x,
+ y: y,
+ width: Math.round((p.eyePosX + p.innerDist) * xPxPerTanAngle) - x,
+ height: Math.round((p.eyePosY + p.topDist) * yPxPerTanAngle) - y
+ };
+ };
+ DeviceInfo.prototype.getUndistortedParams_ = function () {
+ var viewer = this.viewer;
+ var device = this.device;
+ var distortion = this.distortion;
+ var eyeToScreenDistance = viewer.screenLensDistance;
+ var halfLensDistance = viewer.interLensDistance / 2 / eyeToScreenDistance;
+ var screenWidth = device.widthMeters / eyeToScreenDistance;
+ var screenHeight = device.heightMeters / eyeToScreenDistance;
+ var eyePosX = screenWidth / 2 - halfLensDistance;
+ var eyePosY = (viewer.baselineLensDistance - device.bevelMeters) / eyeToScreenDistance;
+ var maxFov = viewer.fov;
+ var viewerMax = distortion.distortInverse(Math.tan(degToRad * maxFov));
+ var outerDist = Math.min(eyePosX, viewerMax);
+ var innerDist = Math.min(halfLensDistance, viewerMax);
+ var bottomDist = Math.min(eyePosY, viewerMax);
+ var topDist = Math.min(screenHeight - eyePosY, viewerMax);
+ return {
+ outerDist: outerDist,
+ innerDist: innerDist,
+ topDist: topDist,
+ bottomDist: bottomDist,
+ eyePosX: eyePosX,
+ eyePosY: eyePosY
+ };
+ };
+ function CardboardViewer(params) {
+ this.id = params.id;
+ this.label = params.label;
+ this.fov = params.fov;
+ this.interLensDistance = params.interLensDistance;
+ this.baselineLensDistance = params.baselineLensDistance;
+ this.screenLensDistance = params.screenLensDistance;
+ this.distortionCoefficients = params.distortionCoefficients;
+ this.inverseCoefficients = params.inverseCoefficients;
+ }
+ DeviceInfo.Viewers = Viewers;
+ var format = 1;
+ var last_updated = "2018-02-20T22:55:10Z";
+ var devices = [{"type":"android","rules":[{"mdmh":"asus/*/Nexus 7/*"},{"ua":"Nexus 7"}],"dpi":[320.8,323],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"asus/*/ASUS_Z00AD/*"},{"ua":"ASUS_Z00AD"}],"dpi":[403,404.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel XL/*"},{"ua":"Pixel XL"}],"dpi":[537.9,533],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel/*"},{"ua":"Pixel"}],"dpi":[432.6,436.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"HTC/*/HTC6435LVW/*"},{"ua":"HTC6435LVW"}],"dpi":[449.7,443.3],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One XL/*"},{"ua":"HTC One XL"}],"dpi":[315.3,314.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"htc/*/Nexus 9/*"},{"ua":"Nexus 9"}],"dpi":289,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One M9/*"},{"ua":"HTC One M9"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One_M8/*"},{"ua":"HTC One_M8"}],"dpi":[449.7,447.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One/*"},{"ua":"HTC One"}],"dpi":472.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Huawei/*/Nexus 6P/*"},{"ua":"Nexus 6P"}],"dpi":[515.1,518],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LENOVO/*/Lenovo PB2-690Y/*"},{"ua":"Lenovo PB2-690Y"}],"dpi":[457.2,454.713],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 5X/*"},{"ua":"Nexus 5X"}],"dpi":[422,419.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGMS345/*"},{"ua":"LGMS345"}],"dpi":[221.7,219.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/LG-D800/*"},{"ua":"LG-D800"}],"dpi":[422,424.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/LG-D850/*"},{"ua":"LG-D850"}],"dpi":[537.9,541.9],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/VS985 4G/*"},{"ua":"VS985 4G"}],"dpi":[537.9,535.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 5/*"},{"ua":"Nexus 5 B"}],"dpi":[442.4,444.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 4/*"},{"ua":"Nexus 4"}],"dpi":[319.8,318.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LG-P769/*"},{"ua":"LG-P769"}],"dpi":[240.6,247.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGMS323/*"},{"ua":"LGMS323"}],"dpi":[206.6,204.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGLS996/*"},{"ua":"LGLS996"}],"dpi":[403.4,401.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/4560MMX/*"},{"ua":"4560MMX"}],"dpi":[240,219.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/A250/*"},{"ua":"Micromax A250"}],"dpi":[480,446.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/Micromax AQ4501/*"},{"ua":"Micromax AQ4501"}],"dpi":240,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/G5/*"},{"ua":"Moto G (5) Plus"}],"dpi":[403.4,403],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/DROID RAZR/*"},{"ua":"DROID RAZR"}],"dpi":[368.1,256.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT830C/*"},{"ua":"XT830C"}],"dpi":[254,255.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1021/*"},{"ua":"XT1021"}],"dpi":[254,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1023/*"},{"ua":"XT1023"}],"dpi":[254,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1028/*"},{"ua":"XT1028"}],"dpi":[326.6,327.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1034/*"},{"ua":"XT1034"}],"dpi":[326.6,328.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1053/*"},{"ua":"XT1053"}],"dpi":[315.3,316.1],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1562/*"},{"ua":"XT1562"}],"dpi":[403.4,402.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/Nexus 6/*"},{"ua":"Nexus 6 B"}],"dpi":[494.3,489.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1063/*"},{"ua":"XT1063"}],"dpi":[295,296.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1064/*"},{"ua":"XT1064"}],"dpi":[295,295.6],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1092/*"},{"ua":"XT1092"}],"dpi":[422,424.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1095/*"},{"ua":"XT1095"}],"dpi":[422,423.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/G4/*"},{"ua":"Moto G (4)"}],"dpi":401,"bw":4,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/A0001/*"},{"ua":"A0001"}],"dpi":[403.4,401],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE E1005/*"},{"ua":"ONE E1005"}],"dpi":[442.4,441.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE A2005/*"},{"ua":"ONE A2005"}],"dpi":[391.9,405.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONEPLUS A5000/*"},{"ua":"ONEPLUS A5000 "}],"dpi":[403.411,399.737],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE A5010/*"},{"ua":"ONEPLUS A5010"}],"dpi":[403,400],"bw":2,"ac":1000},{"type":"android","rules":[{"mdmh":"OPPO/*/X909/*"},{"ua":"X909"}],"dpi":[442.4,444.1],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9082/*"},{"ua":"GT-I9082"}],"dpi":[184.7,185.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G360P/*"},{"ua":"SM-G360P"}],"dpi":[196.7,205.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/Nexus S/*"},{"ua":"Nexus S"}],"dpi":[234.5,229.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300/*"},{"ua":"GT-I9300"}],"dpi":[304.8,303.9],"bw":5,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-T230NU/*"},{"ua":"SM-T230NU"}],"dpi":216,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SGH-T399/*"},{"ua":"SGH-T399"}],"dpi":[217.7,231.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SGH-M919/*"},{"ua":"SGH-M919"}],"dpi":[440.8,437.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N9005/*"},{"ua":"SM-N9005"}],"dpi":[386.4,387],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SAMSUNG-SM-N900A/*"},{"ua":"SAMSUNG-SM-N900A"}],"dpi":[386.4,387.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9500/*"},{"ua":"GT-I9500"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9505/*"},{"ua":"GT-I9505"}],"dpi":439.4,"bw":4,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G900F/*"},{"ua":"SM-G900F"}],"dpi":[415.6,431.6],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G900M/*"},{"ua":"SM-G900M"}],"dpi":[415.6,431.6],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G800F/*"},{"ua":"SM-G800F"}],"dpi":326.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G906S/*"},{"ua":"SM-G906S"}],"dpi":[562.7,572.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300/*"},{"ua":"GT-I9300"}],"dpi":[306.7,304.8],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-T535/*"},{"ua":"SM-T535"}],"dpi":[142.6,136.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920C/*"},{"ua":"SM-N920C"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920P/*"},{"ua":"SM-N920P"}],"dpi":[386.3655,390.144],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920W8/*"},{"ua":"SM-N920W8"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300I/*"},{"ua":"GT-I9300I"}],"dpi":[304.8,305.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9195/*"},{"ua":"GT-I9195"}],"dpi":[249.4,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SPH-L520/*"},{"ua":"SPH-L520"}],"dpi":[249.4,255.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SAMSUNG-SGH-I717/*"},{"ua":"SAMSUNG-SGH-I717"}],"dpi":285.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SPH-D710/*"},{"ua":"SPH-D710"}],"dpi":[217.7,204.2],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-N7100/*"},{"ua":"GT-N7100"}],"dpi":265.1,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SCH-I605/*"},{"ua":"SCH-I605"}],"dpi":265.1,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/Galaxy Nexus/*"},{"ua":"Galaxy Nexus"}],"dpi":[315.3,314.2],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N910H/*"},{"ua":"SM-N910H"}],"dpi":[515.1,518],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N910C/*"},{"ua":"SM-N910C"}],"dpi":[515.2,520.2],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G130M/*"},{"ua":"SM-G130M"}],"dpi":[165.9,164.8],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G928I/*"},{"ua":"SM-G928I"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G920F/*"},{"ua":"SM-G920F"}],"dpi":580.6,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G920P/*"},{"ua":"SM-G920P"}],"dpi":[522.5,577],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G925F/*"},{"ua":"SM-G925F"}],"dpi":580.6,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G925V/*"},{"ua":"SM-G925V"}],"dpi":[522.5,576.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G930F/*"},{"ua":"SM-G930F"}],"dpi":576.6,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G935F/*"},{"ua":"SM-G935F"}],"dpi":533,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G950F/*"},{"ua":"SM-G950F"}],"dpi":[562.707,565.293],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G955U/*"},{"ua":"SM-G955U"}],"dpi":[522.514,525.762],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Sony/*/C6903/*"},{"ua":"C6903"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Sony/*/D6653/*"},{"ua":"D6653"}],"dpi":[428.6,427.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/E6653/*"},{"ua":"E6653"}],"dpi":[428.6,425.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/E6853/*"},{"ua":"E6853"}],"dpi":[403.4,401.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/SGP321/*"},{"ua":"SGP321"}],"dpi":[224.7,224.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"TCT/*/ALCATEL ONE TOUCH Fierce/*"},{"ua":"ALCATEL ONE TOUCH Fierce"}],"dpi":[240,247.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"THL/*/thl 5000/*"},{"ua":"thl 5000"}],"dpi":[480,443.3],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Fly/*/IQ4412/*"},{"ua":"IQ4412"}],"dpi":307.9,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"ZTE/*/ZTE Blade L2/*"},{"ua":"ZTE Blade L2"}],"dpi":240,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"BENEVE/*/VR518/*"},{"ua":"VR518"}],"dpi":480,"bw":3,"ac":500},{"type":"ios","rules":[{"res":[640,960]}],"dpi":[325.1,328.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[640,1136]}],"dpi":[317.1,320.2],"bw":3,"ac":1000},{"type":"ios","rules":[{"res":[750,1334]}],"dpi":326.4,"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1242,2208]}],"dpi":[453.6,458.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1125,2001]}],"dpi":[410.9,415.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1125,2436]}],"dpi":458,"bw":4,"ac":1000}];
+ var DPDB_CACHE = {
+ format: format,
+ last_updated: last_updated,
+ devices: devices
+ };
+ function Dpdb(url, onDeviceParamsUpdated) {
+ this.dpdb = DPDB_CACHE;
+ this.recalculateDeviceParams_();
+ if (url) {
+ this.onDeviceParamsUpdated = onDeviceParamsUpdated;
+ var xhr = new XMLHttpRequest();
+ var obj = this;
+ xhr.open('GET', url, true);
+ xhr.addEventListener('load', function () {
+ obj.loading = false;
+ if (xhr.status >= 200 && xhr.status <= 299) {
+ obj.dpdb = JSON.parse(xhr.response);
+ obj.recalculateDeviceParams_();
+ } else {
+ console.error('Error loading online DPDB!');
+ }
+ });
+ xhr.send();
+ }
+ }
+ Dpdb.prototype.getDeviceParams = function () {
+ return this.deviceParams;
+ };
+ Dpdb.prototype.recalculateDeviceParams_ = function () {
+ var newDeviceParams = this.calcDeviceParams_();
+ if (newDeviceParams) {
+ this.deviceParams = newDeviceParams;
+ if (this.onDeviceParamsUpdated) {
+ this.onDeviceParamsUpdated(this.deviceParams);
+ }
+ } else {
+ console.error('Failed to recalculate device parameters.');
+ }
+ };
+ Dpdb.prototype.calcDeviceParams_ = function () {
+ var db = this.dpdb;
+ if (!db) {
+ console.error('DPDB not available.');
+ return null;
+ }
+ if (db.format != 1) {
+ console.error('DPDB has unexpected format version.');
+ return null;
+ }
+ if (!db.devices || !db.devices.length) {
+ console.error('DPDB does not have a devices section.');
+ return null;
+ }
+ var userAgent = navigator.userAgent || navigator.vendor || window.opera;
+ var width = getScreenWidth();
+ var height = getScreenHeight();
+ if (!db.devices) {
+ console.error('DPDB has no devices section.');
+ return null;
+ }
+ for (var i = 0; i < db.devices.length; i++) {
+ var device = db.devices[i];
+ if (!device.rules) {
+ console.warn('Device[' + i + '] has no rules section.');
+ continue;
+ }
+ if (device.type != 'ios' && device.type != 'android') {
+ console.warn('Device[' + i + '] has invalid type.');
+ continue;
+ }
+ if (isIOS() != (device.type == 'ios')) continue;
+ var matched = false;
+ for (var j = 0; j < device.rules.length; j++) {
+ var rule = device.rules[j];
+ if (this.matchRule_(rule, userAgent, width, height)) {
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) continue;
+ var xdpi = device.dpi[0] || device.dpi;
+ var ydpi = device.dpi[1] || device.dpi;
+ return new DeviceParams({ xdpi: xdpi, ydpi: ydpi, bevelMm: device.bw });
+ }
+ console.warn('No DPDB device match.');
+ return null;
+ };
+ Dpdb.prototype.matchRule_ = function (rule, ua, screenWidth, screenHeight) {
+ if (!rule.ua && !rule.res) return false;
+ if (rule.ua && ua.indexOf(rule.ua) < 0) return false;
+ if (rule.res) {
+ if (!rule.res[0] || !rule.res[1]) return false;
+ var resX = rule.res[0];
+ var resY = rule.res[1];
+ if (Math.min(screenWidth, screenHeight) != Math.min(resX, resY) || Math.max(screenWidth, screenHeight) != Math.max(resX, resY)) {
+ return false;
+ }
+ }
+ return true;
+ };
+ function DeviceParams(params) {
+ this.xdpi = params.xdpi;
+ this.ydpi = params.ydpi;
+ this.bevelMm = params.bevelMm;
+ }
+ function SensorSample(sample, timestampS) {
+ this.set(sample, timestampS);
+ }
+ SensorSample.prototype.set = function (sample, timestampS) {
+ this.sample = sample;
+ this.timestampS = timestampS;
+ };
+ SensorSample.prototype.copy = function (sensorSample) {
+ this.set(sensorSample.sample, sensorSample.timestampS);
+ };
+ function ComplementaryFilter(kFilter, isDebug) {
+ this.kFilter = kFilter;
+ this.isDebug = isDebug;
+ this.currentAccelMeasurement = new SensorSample();
+ this.currentGyroMeasurement = new SensorSample();
+ this.previousGyroMeasurement = new SensorSample();
+ if (isIOS()) {
+ this.filterQ = new Quaternion(-1, 0, 0, 1);
+ } else {
+ this.filterQ = new Quaternion(1, 0, 0, 1);
+ }
+ this.previousFilterQ = new Quaternion();
+ this.previousFilterQ.copy(this.filterQ);
+ this.accelQ = new Quaternion();
+ this.isOrientationInitialized = false;
+ this.estimatedGravity = new Vector3();
+ this.measuredGravity = new Vector3();
+ this.gyroIntegralQ = new Quaternion();
+ }
+ ComplementaryFilter.prototype.addAccelMeasurement = function (vector, timestampS) {
+ this.currentAccelMeasurement.set(vector, timestampS);
+ };
+ ComplementaryFilter.prototype.addGyroMeasurement = function (vector, timestampS) {
+ this.currentGyroMeasurement.set(vector, timestampS);
+ var deltaT = timestampS - this.previousGyroMeasurement.timestampS;
+ if (isTimestampDeltaValid(deltaT)) {
+ this.run_();
+ }
+ this.previousGyroMeasurement.copy(this.currentGyroMeasurement);
+ };
+ ComplementaryFilter.prototype.run_ = function () {
+ if (!this.isOrientationInitialized) {
+ this.accelQ = this.accelToQuaternion_(this.currentAccelMeasurement.sample);
+ this.previousFilterQ.copy(this.accelQ);
+ this.isOrientationInitialized = true;
+ return;
+ }
+ var deltaT = this.currentGyroMeasurement.timestampS - this.previousGyroMeasurement.timestampS;
+ var gyroDeltaQ = this.gyroToQuaternionDelta_(this.currentGyroMeasurement.sample, deltaT);
+ this.gyroIntegralQ.multiply(gyroDeltaQ);
+ this.filterQ.copy(this.previousFilterQ);
+ this.filterQ.multiply(gyroDeltaQ);
+ var invFilterQ = new Quaternion();
+ invFilterQ.copy(this.filterQ);
+ invFilterQ.inverse();
+ this.estimatedGravity.set(0, 0, -1);
+ this.estimatedGravity.applyQuaternion(invFilterQ);
+ this.estimatedGravity.normalize();
+ this.measuredGravity.copy(this.currentAccelMeasurement.sample);
+ this.measuredGravity.normalize();
+ var deltaQ = new Quaternion();
+ deltaQ.setFromUnitVectors(this.estimatedGravity, this.measuredGravity);
+ deltaQ.inverse();
+ if (this.isDebug) {
+ console.log('Delta: %d deg, G_est: (%s, %s, %s), G_meas: (%s, %s, %s)', radToDeg * getQuaternionAngle(deltaQ), this.estimatedGravity.x.toFixed(1), this.estimatedGravity.y.toFixed(1), this.estimatedGravity.z.toFixed(1), this.measuredGravity.x.toFixed(1), this.measuredGravity.y.toFixed(1), this.measuredGravity.z.toFixed(1));
+ }
+ var targetQ = new Quaternion();
+ targetQ.copy(this.filterQ);
+ targetQ.multiply(deltaQ);
+ this.filterQ.slerp(targetQ, 1 - this.kFilter);
+ this.previousFilterQ.copy(this.filterQ);
+ };
+ ComplementaryFilter.prototype.getOrientation = function () {
+ return this.filterQ;
+ };
+ ComplementaryFilter.prototype.accelToQuaternion_ = function (accel) {
+ var normAccel = new Vector3();
+ normAccel.copy(accel);
+ normAccel.normalize();
+ var quat = new Quaternion();
+ quat.setFromUnitVectors(new Vector3(0, 0, -1), normAccel);
+ quat.inverse();
+ return quat;
+ };
+ ComplementaryFilter.prototype.gyroToQuaternionDelta_ = function (gyro, dt) {
+ var quat = new Quaternion();
+ var axis = new Vector3();
+ axis.copy(gyro);
+ axis.normalize();
+ quat.setFromAxisAngle(axis, gyro.length() * dt);
+ return quat;
+ };
+ function PosePredictor(predictionTimeS, isDebug) {
+ this.predictionTimeS = predictionTimeS;
+ this.isDebug = isDebug;
+ this.previousQ = new Quaternion();
+ this.previousTimestampS = null;
+ this.deltaQ = new Quaternion();
+ this.outQ = new Quaternion();
+ }
+ PosePredictor.prototype.getPrediction = function (currentQ, gyro, timestampS) {
+ if (!this.previousTimestampS) {
+ this.previousQ.copy(currentQ);
+ this.previousTimestampS = timestampS;
+ return currentQ;
+ }
+ var axis = new Vector3();
+ axis.copy(gyro);
+ axis.normalize();
+ var angularSpeed = gyro.length();
+ if (angularSpeed < degToRad * 20) {
+ if (this.isDebug) {
+ console.log('Moving slowly, at %s deg/s: no prediction', (radToDeg * angularSpeed).toFixed(1));
+ }
+ this.outQ.copy(currentQ);
+ this.previousQ.copy(currentQ);
+ return this.outQ;
+ }
+ var predictAngle = angularSpeed * this.predictionTimeS;
+ this.deltaQ.setFromAxisAngle(axis, predictAngle);
+ this.outQ.copy(this.previousQ);
+ this.outQ.multiply(this.deltaQ);
+ this.previousQ.copy(currentQ);
+ this.previousTimestampS = timestampS;
+ return this.outQ;
+ };
+ function FusionPoseSensor(kFilter, predictionTime, yawOnly, isDebug) {
+ this.yawOnly = yawOnly;
+ this.accelerometer = new Vector3();
+ this.gyroscope = new Vector3();
+ this.filter = new ComplementaryFilter(kFilter, isDebug);
+ this.posePredictor = new PosePredictor(predictionTime, isDebug);
+ this.isFirefoxAndroid = isFirefoxAndroid();
+ this.isIOS = isIOS();
+ var chromeVersion = getChromeVersion();
+ this.isDeviceMotionInRadians = !this.isIOS && chromeVersion && chromeVersion < 66;
+ this.isWithoutDeviceMotion = isChromeWithoutDeviceMotion();
+ this.filterToWorldQ = new Quaternion();
+ if (isIOS()) {
+ this.filterToWorldQ.setFromAxisAngle(new Vector3(1, 0, 0), Math.PI / 2);
+ } else {
+ this.filterToWorldQ.setFromAxisAngle(new Vector3(1, 0, 0), -Math.PI / 2);
+ }
+ this.inverseWorldToScreenQ = new Quaternion();
+ this.worldToScreenQ = new Quaternion();
+ this.originalPoseAdjustQ = new Quaternion();
+ this.originalPoseAdjustQ.setFromAxisAngle(new Vector3(0, 0, 1), -window.orientation * Math.PI / 180);
+ this.setScreenTransform_();
+ if (isLandscapeMode()) {
+ this.filterToWorldQ.multiply(this.inverseWorldToScreenQ);
+ }
+ this.resetQ = new Quaternion();
+ this.orientationOut_ = new Float32Array(4);
+ this.start();
+ }
+ FusionPoseSensor.prototype.getPosition = function () {
+ return null;
+ };
+ FusionPoseSensor.prototype.getOrientation = function () {
+ var orientation = void 0;
+ if (this.isWithoutDeviceMotion && this._deviceOrientationQ) {
+ this.deviceOrientationFixQ = this.deviceOrientationFixQ || function () {
+ var z = new Quaternion().setFromAxisAngle(new Vector3(0, 0, -1), 0);
+ var y = new Quaternion();
+ if (window.orientation === -90) {
+ y.setFromAxisAngle(new Vector3(0, 1, 0), Math.PI / -2);
+ } else {
+ y.setFromAxisAngle(new Vector3(0, 1, 0), Math.PI / 2);
+ }
+ return z.multiply(y);
+ }();
+ this.deviceOrientationFilterToWorldQ = this.deviceOrientationFilterToWorldQ || function () {
+ var q = new Quaternion();
+ q.setFromAxisAngle(new Vector3(1, 0, 0), -Math.PI / 2);
+ return q;
+ }();
+ orientation = this._deviceOrientationQ;
+ var out = new Quaternion();
+ out.copy(orientation);
+ out.multiply(this.deviceOrientationFilterToWorldQ);
+ out.multiply(this.resetQ);
+ out.multiply(this.worldToScreenQ);
+ out.multiplyQuaternions(this.deviceOrientationFixQ, out);
+ if (this.yawOnly) {
+ out.x = 0;
+ out.z = 0;
+ out.normalize();
+ }
+ this.orientationOut_[0] = out.x;
+ this.orientationOut_[1] = out.y;
+ this.orientationOut_[2] = out.z;
+ this.orientationOut_[3] = out.w;
+ return this.orientationOut_;
+ } else {
+ var filterOrientation = this.filter.getOrientation();
+ orientation = this.posePredictor.getPrediction(filterOrientation, this.gyroscope, this.previousTimestampS);
+ }
+ var out = new Quaternion();
+ out.copy(this.filterToWorldQ);
+ out.multiply(this.resetQ);
+ out.multiply(orientation);
+ out.multiply(this.worldToScreenQ);
+ if (this.yawOnly) {
+ out.x = 0;
+ out.z = 0;
+ out.normalize();
+ }
+ this.orientationOut_[0] = out.x;
+ this.orientationOut_[1] = out.y;
+ this.orientationOut_[2] = out.z;
+ this.orientationOut_[3] = out.w;
+ return this.orientationOut_;
+ };
+ FusionPoseSensor.prototype.resetPose = function () {
+ this.resetQ.copy(this.filter.getOrientation());
+ this.resetQ.x = 0;
+ this.resetQ.y = 0;
+ this.resetQ.z *= -1;
+ this.resetQ.normalize();
+ if (isLandscapeMode()) {
+ this.resetQ.multiply(this.inverseWorldToScreenQ);
+ }
+ this.resetQ.multiply(this.originalPoseAdjustQ);
+ };
+ FusionPoseSensor.prototype.onDeviceOrientation_ = function (e) {
+ this._deviceOrientationQ = this._deviceOrientationQ || new Quaternion();
+ var alpha = e.alpha,
+ beta = e.beta,
+ gamma = e.gamma;
+ alpha = (alpha || 0) * Math.PI / 180;
+ beta = (beta || 0) * Math.PI / 180;
+ gamma = (gamma || 0) * Math.PI / 180;
+ this._deviceOrientationQ.setFromEulerYXZ(beta, alpha, -gamma);
+ };
+ FusionPoseSensor.prototype.onDeviceMotion_ = function (deviceMotion) {
+ this.updateDeviceMotion_(deviceMotion);
+ };
+ FusionPoseSensor.prototype.updateDeviceMotion_ = function (deviceMotion) {
+ var accGravity = deviceMotion.accelerationIncludingGravity;
+ var rotRate = deviceMotion.rotationRate;
+ var timestampS = deviceMotion.timeStamp / 1000;
+ var deltaS = timestampS - this.previousTimestampS;
+ if (deltaS < 0) {
+ warnOnce('fusion-pose-sensor:invalid:non-monotonic', 'Invalid timestamps detected: non-monotonic timestamp from devicemotion');
+ this.previousTimestampS = timestampS;
+ return;
+ } else if (deltaS <= MIN_TIMESTEP || deltaS > MAX_TIMESTEP) {
+ warnOnce('fusion-pose-sensor:invalid:outside-threshold', 'Invalid timestamps detected: Timestamp from devicemotion outside expected range.');
+ this.previousTimestampS = timestampS;
+ return;
+ }
+ this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z);
+ if (isR7()) {
+ this.gyroscope.set(-rotRate.beta, rotRate.alpha, rotRate.gamma);
+ } else {
+ this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma);
+ }
+ if (!this.isDeviceMotionInRadians) {
+ this.gyroscope.multiplyScalar(Math.PI / 180);
+ }
+ this.filter.addAccelMeasurement(this.accelerometer, timestampS);
+ this.filter.addGyroMeasurement(this.gyroscope, timestampS);
+ this.previousTimestampS = timestampS;
+ };
+ FusionPoseSensor.prototype.onOrientationChange_ = function (screenOrientation) {
+ this.setScreenTransform_();
+ };
+ FusionPoseSensor.prototype.onMessage_ = function (event) {
+ var message = event.data;
+ if (!message || !message.type) {
+ return;
+ }
+ var type = message.type.toLowerCase();
+ if (type !== 'devicemotion') {
+ return;
+ }
+ this.updateDeviceMotion_(message.deviceMotionEvent);
+ };
+ FusionPoseSensor.prototype.setScreenTransform_ = function () {
+ this.worldToScreenQ.set(0, 0, 0, 1);
+ switch (window.orientation) {
+ case 0:
+ break;
+ case 90:
+ this.worldToScreenQ.setFromAxisAngle(new Vector3(0, 0, 1), -Math.PI / 2);
+ break;
+ case -90:
+ this.worldToScreenQ.setFromAxisAngle(new Vector3(0, 0, 1), Math.PI / 2);
+ break;
+ case 180:
+ break;
+ }
+ this.inverseWorldToScreenQ.copy(this.worldToScreenQ);
+ this.inverseWorldToScreenQ.inverse();
+ };
+ FusionPoseSensor.prototype.start = function () {
+ this.onDeviceMotionCallback_ = this.onDeviceMotion_.bind(this);
+ this.onOrientationChangeCallback_ = this.onOrientationChange_.bind(this);
+ this.onMessageCallback_ = this.onMessage_.bind(this);
+ this.onDeviceOrientationCallback_ = this.onDeviceOrientation_.bind(this);
+ if (isIOS() && isInsideCrossOriginIFrame()) {
+ window.addEventListener('message', this.onMessageCallback_);
+ }
+ window.addEventListener('orientationchange', this.onOrientationChangeCallback_);
+ if (this.isWithoutDeviceMotion) {
+ window.addEventListener('deviceorientation', this.onDeviceOrientationCallback_);
+ } else {
+ window.addEventListener('devicemotion', this.onDeviceMotionCallback_);
+ }
+ };
+ FusionPoseSensor.prototype.stop = function () {
+ window.removeEventListener('devicemotion', this.onDeviceMotionCallback_);
+ window.removeEventListener('deviceorientation', this.onDeviceOrientationCallback_);
+ window.removeEventListener('orientationchange', this.onOrientationChangeCallback_);
+ window.removeEventListener('message', this.onMessageCallback_);
+ };
+ var SENSOR_FREQUENCY = 60;
+ var X_AXIS = new Vector3(1, 0, 0);
+ var Z_AXIS = new Vector3(0, 0, 1);
+ var orientation = {};
+ if (screen.orientation) {
+ orientation = screen.orientation;
+ } else if (screen.msOrientation) {
+ orientation = screen.msOrientation;
+ } else {
+ Object.defineProperty(orientation, 'angle', {
+ get: function get$$1() {
+ return window.orientation || 0;
+ }
+ });
+ }
+ var SENSOR_TO_VR = new Quaternion();
+ SENSOR_TO_VR.setFromAxisAngle(X_AXIS, -Math.PI / 2);
+ SENSOR_TO_VR.multiply(new Quaternion().setFromAxisAngle(Z_AXIS, Math.PI / 2));
+ var PoseSensor = function () {
+ function PoseSensor(config) {
+ classCallCheck(this, PoseSensor);
+ this.config = config;
+ this.sensor = null;
+ this.fusionSensor = null;
+ this._out = new Float32Array(4);
+ this.api = null;
+ this.errors = [];
+ this._sensorQ = new Quaternion();
+ this._worldToScreenQ = new Quaternion();
+ this._outQ = new Quaternion();
+ this._onSensorRead = this._onSensorRead.bind(this);
+ this._onSensorError = this._onSensorError.bind(this);
+ this._onOrientationChange = this._onOrientationChange.bind(this);
+ this._onOrientationChange();
+ this.init();
+ }
+ createClass(PoseSensor, [{
+ key: 'init',
+ value: function init() {
+ var sensor = null;
+ try {
+ sensor = new RelativeOrientationSensor({ frequency: SENSOR_FREQUENCY });
+ sensor.addEventListener('error', this._onSensorError);
+ } catch (error) {
+ this.errors.push(error);
+ if (error.name === 'SecurityError') {
+ console.error('Cannot construct sensors due to the Feature Policy');
+ console.warn('Attempting to fall back using "devicemotion"; however this will ' + 'fail in the future without correct permissions.');
+ this.useDeviceMotion();
+ } else if (error.name === 'ReferenceError') {
+ this.useDeviceMotion();
+ } else {
+ console.error(error);
+ }
+ }
+ if (sensor) {
+ this.api = 'sensor';
+ this.sensor = sensor;
+ this.sensor.addEventListener('reading', this._onSensorRead);
+ this.sensor.start();
+ }
+ window.addEventListener('orientationchange', this._onOrientationChange);
+ }
+ }, {
+ key: 'useDeviceMotion',
+ value: function useDeviceMotion() {
+ this.api = 'devicemotion';
+ this.fusionSensor = new FusionPoseSensor(this.config.K_FILTER, this.config.PREDICTION_TIME_S, this.config.YAW_ONLY, this.config.DEBUG);
+ if (this.sensor) {
+ this.sensor.removeEventListener('reading', this._onSensorRead);
+ this.sensor.removeEventListener('error', this._onSensorError);
+ this.sensor = null;
+ }
+ }
+ }, {
+ key: 'getOrientation',
+ value: function getOrientation() {
+ if (this.fusionSensor) {
+ return this.fusionSensor.getOrientation();
+ }
+ if (!this.sensor || !this.sensor.quaternion) {
+ this._out[0] = this._out[1] = this._out[2] = 0;
+ this._out[3] = 1;
+ return this._out;
+ }
+ var q = this.sensor.quaternion;
+ this._sensorQ.set(q[0], q[1], q[2], q[3]);
+ var out = this._outQ;
+ out.copy(SENSOR_TO_VR);
+ out.multiply(this._sensorQ);
+ out.multiply(this._worldToScreenQ);
+ if (this.config.YAW_ONLY) {
+ out.x = out.z = 0;
+ out.normalize();
+ }
+ this._out[0] = out.x;
+ this._out[1] = out.y;
+ this._out[2] = out.z;
+ this._out[3] = out.w;
+ return this._out;
+ }
+ }, {
+ key: '_onSensorError',
+ value: function _onSensorError(event) {
+ this.errors.push(event.error);
+ if (event.error.name === 'NotAllowedError') {
+ console.error('Permission to access sensor was denied');
+ } else if (event.error.name === 'NotReadableError') {
+ console.error('Sensor could not be read');
+ } else {
+ console.error(event.error);
+ }
+ this.useDeviceMotion();
+ }
+ }, {
+ key: '_onSensorRead',
+ value: function _onSensorRead() {}
+ }, {
+ key: '_onOrientationChange',
+ value: function _onOrientationChange() {
+ var angle = -orientation.angle * Math.PI / 180;
+ this._worldToScreenQ.setFromAxisAngle(Z_AXIS, angle);
+ }
+ }]);
+ return PoseSensor;
+ }();
+ var rotateInstructionsAsset = 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjE5OHB4IiBoZWlnaHQ9IjI0MHB4IiB2aWV3Qm94PSIwIDAgMTk4IDI0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpza2V0Y2g9Imh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaC9ucyI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDMuMy4zICgxMjA4MSkgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+dHJhbnNpdGlvbjwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPjwvZGVmcz4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHNrZXRjaDp0eXBlPSJNU1BhZ2UiPgogICAgICAgIDxnIGlkPSJ0cmFuc2l0aW9uIiBza2V0Y2g6dHlwZT0iTVNBcnRib2FyZEdyb3VwIj4KICAgICAgICAgICAgPGcgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTQtKy1JbXBvcnRlZC1MYXllcnMtQ29weS0rLUltcG9ydGVkLUxheWVycy1Db3B5LTItQ29weSIgc2tldGNoOnR5cGU9Ik1TTGF5ZXJHcm91cCI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHktNCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEwNy4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjYyNSwyLjUyNyBDMTQ5LjYyNSwyLjUyNyAxNTUuODA1LDYuMDk2IDE1Ni4zNjIsNi40MTggTDE1Ni4zNjIsNy4zMDQgQzE1Ni4zNjIsNy40ODEgMTU2LjM3NSw3LjY2NCAxNTYuNCw3Ljg1MyBDMTU2LjQxLDcuOTM0IDE1Ni40Miw4LjAxNSAxNTYuNDI3LDguMDk1IEMxNTYuNTY3LDkuNTEgMTU3LjQwMSwxMS4wOTMgMTU4LjUzMiwxMi4wOTQgTDE2NC4yNTIsMTcuMTU2IEwxNjQuMzMzLDE3LjA2NiBDMTY0LjMzMywxNy4wNjYgMTY4LjcxNSwxNC41MzYgMTY5LjU2OCwxNC4wNDIgQzE3MS4wMjUsMTQuODgzIDE5NS41MzgsMjkuMDM1IDE5NS41MzgsMjkuMDM1IEwxOTUuNTM4LDgzLjAzNiBDMTk1LjUzOCw4My44MDcgMTk1LjE1Miw4NC4yNTMgMTk0LjU5LDg0LjI1MyBDMTk0LjM1Nyw4NC4yNTMgMTk0LjA5NSw4NC4xNzcgMTkzLjgxOCw4NC4wMTcgTDE2OS44NTEsNzAuMTc5IEwxNjkuODM3LDcwLjIwMyBMMTQyLjUxNSw4NS45NzggTDE0MS42NjUsODQuNjU1IEMxMzYuOTM0LDgzLjEyNiAxMzEuOTE3LDgxLjkxNSAxMjYuNzE0LDgxLjA0NSBDMTI2LjcwOSw4MS4wNiAxMjYuNzA3LDgxLjA2OSAxMjYuNzA3LDgxLjA2OSBMMTIxLjY0LDk4LjAzIEwxMTMuNzQ5LDEwMi41ODYgTDExMy43MTIsMTAyLjUyMyBMMTEzLjcxMiwxMzAuMTEzIEMxMTMuNzEyLDEzMC44ODUgMTEzLjMyNiwxMzEuMzMgMTEyLjc2NCwxMzEuMzMgQzExMi41MzIsMTMxLjMzIDExMi4yNjksMTMxLjI1NCAxMTEuOTkyLDEzMS4wOTQgTDY5LjUxOSwxMDYuNTcyIEM2OC41NjksMTA2LjAyMyA2Ny43OTksMTA0LjY5NSA2Ny43OTksMTAzLjYwNSBMNjcuNzk5LDEwMi41NyBMNjcuNzc4LDEwMi42MTcgQzY3LjI3LDEwMi4zOTMgNjYuNjQ4LDEwMi4yNDkgNjUuOTYyLDEwMi4yMTggQzY1Ljg3NSwxMDIuMjE0IDY1Ljc4OCwxMDIuMjEyIDY1LjcwMSwxMDIuMjEyIEM2NS42MDYsMTAyLjIxMiA2NS41MTEsMTAyLjIxNSA2NS40MTYsMTAyLjIxOSBDNjUuMTk1LDEwMi4yMjkgNjQuOTc0LDEwMi4yMzUgNjQuNzU0LDEwMi4yMzUgQzY0LjMzMSwxMDIuMjM1IDYzLjkxMSwxMDIuMjE2IDYzLjQ5OCwxMDIuMTc4IEM2MS44NDMsMTAyLjAyNSA2MC4yOTgsMTAxLjU3OCA1OS4wOTQsMTAwLjg4MiBMMTIuNTE4LDczLjk5MiBMMTIuNTIzLDc0LjAwNCBMMi4yNDUsNTUuMjU0IEMxLjI0NCw1My40MjcgMi4wMDQsNTEuMDM4IDMuOTQzLDQ5LjkxOCBMNTkuOTU0LDE3LjU3MyBDNjAuNjI2LDE3LjE4NSA2MS4zNSwxNy4wMDEgNjIuMDUzLDE3LjAwMSBDNjMuMzc5LDE3LjAwMSA2NC42MjUsMTcuNjYgNjUuMjgsMTguODU0IEw2NS4yODUsMTguODUxIEw2NS41MTIsMTkuMjY0IEw2NS41MDYsMTkuMjY4IEM2NS45MDksMjAuMDAzIDY2LjQwNSwyMC42OCA2Ni45ODMsMjEuMjg2IEw2Ny4yNiwyMS41NTYgQzY5LjE3NCwyMy40MDYgNzEuNzI4LDI0LjM1NyA3NC4zNzMsMjQuMzU3IEM3Ni4zMjIsMjQuMzU3IDc4LjMyMSwyMy44NCA4MC4xNDgsMjIuNzg1IEM4MC4xNjEsMjIuNzg1IDg3LjQ2NywxOC41NjYgODcuNDY3LDE4LjU2NiBDODguMTM5LDE4LjE3OCA4OC44NjMsMTcuOTk0IDg5LjU2NiwxNy45OTQgQzkwLjg5MiwxNy45OTQgOTIuMTM4LDE4LjY1MiA5Mi43OTIsMTkuODQ3IEw5Ni4wNDIsMjUuNzc1IEw5Ni4wNjQsMjUuNzU3IEwxMDIuODQ5LDI5LjY3NCBMMTAyLjc0NCwyOS40OTIgTDE0OS42MjUsMi41MjcgTTE0OS42MjUsMC44OTIgQzE0OS4zNDMsMC44OTIgMTQ5LjA2MiwwLjk2NSAxNDguODEsMS4xMSBMMTAyLjY0MSwyNy42NjYgTDk3LjIzMSwyNC41NDIgTDk0LjIyNiwxOS4wNjEgQzkzLjMxMywxNy4zOTQgOTEuNTI3LDE2LjM1OSA4OS41NjYsMTYuMzU4IEM4OC41NTUsMTYuMzU4IDg3LjU0NiwxNi42MzIgODYuNjQ5LDE3LjE1IEM4My44NzgsMTguNzUgNzkuNjg3LDIxLjE2OSA3OS4zNzQsMjEuMzQ1IEM3OS4zNTksMjEuMzUzIDc5LjM0NSwyMS4zNjEgNzkuMzMsMjEuMzY5IEM3Ny43OTgsMjIuMjU0IDc2LjA4NCwyMi43MjIgNzQuMzczLDIyLjcyMiBDNzIuMDgxLDIyLjcyMiA2OS45NTksMjEuODkgNjguMzk3LDIwLjM4IEw2OC4xNDUsMjAuMTM1IEM2Ny43MDYsMTkuNjcyIDY3LjMyMywxOS4xNTYgNjcuMDA2LDE4LjYwMSBDNjYuOTg4LDE4LjU1OSA2Ni45NjgsMTguNTE5IDY2Ljk0NiwxOC40NzkgTDY2LjcxOSwxOC4wNjUgQzY2LjY5LDE4LjAxMiA2Ni42NTgsMTcuOTYgNjYuNjI0LDE3LjkxMSBDNjUuNjg2LDE2LjMzNyA2My45NTEsMTUuMzY2IDYyLjA1MywxNS4zNjYgQzYxLjA0MiwxNS4zNjYgNjAuMDMzLDE1LjY0IDU5LjEzNiwxNi4xNTggTDMuMTI1LDQ4LjUwMiBDMC40MjYsNTAuMDYxIC0wLjYxMyw1My40NDIgMC44MTEsNTYuMDQgTDExLjA4OSw3NC43OSBDMTEuMjY2LDc1LjExMyAxMS41MzcsNzUuMzUzIDExLjg1LDc1LjQ5NCBMNTguMjc2LDEwMi4yOTggQzU5LjY3OSwxMDMuMTA4IDYxLjQzMywxMDMuNjMgNjMuMzQ4LDEwMy44MDYgQzYzLjgxMiwxMDMuODQ4IDY0LjI4NSwxMDMuODcgNjQuNzU0LDEwMy44NyBDNjUsMTAzLjg3IDY1LjI0OSwxMDMuODY0IDY1LjQ5NCwxMDMuODUyIEM2NS41NjMsMTAzLjg0OSA2NS42MzIsMTAzLjg0NyA2NS43MDEsMTAzLjg0NyBDNjUuNzY0LDEwMy44NDcgNjUuODI4LDEwMy44NDkgNjUuODksMTAzLjg1MiBDNjUuOTg2LDEwMy44NTYgNjYuMDgsMTAzLjg2MyA2Ni4xNzMsMTAzLjg3NCBDNjYuMjgyLDEwNS40NjcgNjcuMzMyLDEwNy4xOTcgNjguNzAyLDEwNy45ODggTDExMS4xNzQsMTMyLjUxIEMxMTEuNjk4LDEzMi44MTIgMTEyLjIzMiwxMzIuOTY1IDExMi43NjQsMTMyLjk2NSBDMTE0LjI2MSwxMzIuOTY1IDExNS4zNDcsMTMxLjc2NSAxMTUuMzQ3LDEzMC4xMTMgTDExNS4zNDcsMTAzLjU1MSBMMTIyLjQ1OCw5OS40NDYgQzEyMi44MTksOTkuMjM3IDEyMy4wODcsOTguODk4IDEyMy4yMDcsOTguNDk4IEwxMjcuODY1LDgyLjkwNSBDMTMyLjI3OSw4My43MDIgMTM2LjU1Nyw4NC43NTMgMTQwLjYwNyw4Ni4wMzMgTDE0MS4xNCw4Ni44NjIgQzE0MS40NTEsODcuMzQ2IDE0MS45NzcsODcuNjEzIDE0Mi41MTYsODcuNjEzIEMxNDIuNzk0LDg3LjYxMyAxNDMuMDc2LDg3LjU0MiAxNDMuMzMzLDg3LjM5MyBMMTY5Ljg2NSw3Mi4wNzYgTDE5Myw4NS40MzMgQzE5My41MjMsODUuNzM1IDE5NC4wNTgsODUuODg4IDE5NC41OSw4NS44ODggQzE5Ni4wODcsODUuODg4IDE5Ny4xNzMsODQuNjg5IDE5Ny4xNzMsODMuMDM2IEwxOTcuMTczLDI5LjAzNSBDMTk3LjE3MywyOC40NTEgMTk2Ljg2MSwyNy45MTEgMTk2LjM1NSwyNy42MTkgQzE5Ni4zNTUsMjcuNjE5IDE3MS44NDMsMTMuNDY3IDE3MC4zODUsMTIuNjI2IEMxNzAuMTMyLDEyLjQ4IDE2OS44NSwxMi40MDcgMTY5LjU2OCwxMi40MDcgQzE2OS4yODUsMTIuNDA3IDE2OS4wMDIsMTIuNDgxIDE2OC43NDksMTIuNjI3IEMxNjguMTQzLDEyLjk3OCAxNjUuNzU2LDE0LjM1NyAxNjQuNDI0LDE1LjEyNSBMMTU5LjYxNSwxMC44NyBDMTU4Ljc5NiwxMC4xNDUgMTU4LjE1NCw4LjkzNyAxNTguMDU0LDcuOTM0IEMxNTguMDQ1LDcuODM3IDE1OC4wMzQsNy43MzkgMTU4LjAyMSw3LjY0IEMxNTguMDA1LDcuNTIzIDE1Ny45OTgsNy40MSAxNTcuOTk4LDcuMzA0IEwxNTcuOTk4LDYuNDE4IEMxNTcuOTk4LDUuODM0IDE1Ny42ODYsNS4yOTUgMTU3LjE4MSw1LjAwMiBDMTU2LjYyNCw0LjY4IDE1MC40NDIsMS4xMTEgMTUwLjQ0MiwxLjExMSBDMTUwLjE4OSwwLjk2NSAxNDkuOTA3LDAuODkyIDE0OS42MjUsMC44OTIiIGlkPSJGaWxsLTEiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTYuMDI3LDI1LjYzNiBMMTQyLjYwMyw1Mi41MjcgQzE0My44MDcsNTMuMjIyIDE0NC41ODIsNTQuMTE0IDE0NC44NDUsNTUuMDY4IEwxNDQuODM1LDU1LjA3NSBMNjMuNDYxLDEwMi4wNTcgTDYzLjQ2LDEwMi4wNTcgQzYxLjgwNiwxMDEuOTA1IDYwLjI2MSwxMDEuNDU3IDU5LjA1NywxMDAuNzYyIEwxMi40ODEsNzMuODcxIEw5Ni4wMjcsMjUuNjM2IiBpZD0iRmlsbC0yIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYzLjQ2MSwxMDIuMTc0IEM2My40NTMsMTAyLjE3NCA2My40NDYsMTAyLjE3NCA2My40MzksMTAyLjE3MiBDNjEuNzQ2LDEwMi4wMTYgNjAuMjExLDEwMS41NjMgNTguOTk4LDEwMC44NjMgTDEyLjQyMiw3My45NzMgQzEyLjM4Niw3My45NTIgMTIuMzY0LDczLjkxNCAxMi4zNjQsNzMuODcxIEMxMi4zNjQsNzMuODMgMTIuMzg2LDczLjc5MSAxMi40MjIsNzMuNzcgTDk1Ljk2OCwyNS41MzUgQzk2LjAwNCwyNS41MTQgOTYuMDQ5LDI1LjUxNCA5Ni4wODUsMjUuNTM1IEwxNDIuNjYxLDUyLjQyNiBDMTQzLjg4OCw1My4xMzQgMTQ0LjY4Miw1NC4wMzggMTQ0Ljk1Nyw1NS4wMzcgQzE0NC45Nyw1NS4wODMgMTQ0Ljk1Myw1NS4xMzMgMTQ0LjkxNSw1NS4xNjEgQzE0NC45MTEsNTUuMTY1IDE0NC44OTgsNTUuMTc0IDE0NC44OTQsNTUuMTc3IEw2My41MTksMTAyLjE1OCBDNjMuNTAxLDEwMi4xNjkgNjMuNDgxLDEwMi4xNzQgNjMuNDYxLDEwMi4xNzQgTDYzLjQ2MSwxMDIuMTc0IFogTTEyLjcxNCw3My44NzEgTDU5LjExNSwxMDAuNjYxIEM2MC4yOTMsMTAxLjM0MSA2MS43ODYsMTAxLjc4MiA2My40MzUsMTAxLjkzNyBMMTQ0LjcwNyw1NS4wMTUgQzE0NC40MjgsNTQuMTA4IDE0My42ODIsNTMuMjg1IDE0Mi41NDQsNTIuNjI4IEw5Ni4wMjcsMjUuNzcxIEwxMi43MTQsNzMuODcxIEwxMi43MTQsNzMuODcxIFoiIGlkPSJGaWxsLTMiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ4LjMyNyw1OC40NzEgQzE0OC4xNDUsNTguNDggMTQ3Ljk2Miw1OC40OCAxNDcuNzgxLDU4LjQ3MiBDMTQ1Ljg4Nyw1OC4zODkgMTQ0LjQ3OSw1Ny40MzQgMTQ0LjYzNiw1Ni4zNCBDMTQ0LjY4OSw1NS45NjcgMTQ0LjY2NCw1NS41OTcgMTQ0LjU2NCw1NS4yMzUgTDYzLjQ2MSwxMDIuMDU3IEM2NC4wODksMTAyLjExNSA2NC43MzMsMTAyLjEzIDY1LjM3OSwxMDIuMDk5IEM2NS41NjEsMTAyLjA5IDY1Ljc0MywxMDIuMDkgNjUuOTI1LDEwMi4wOTggQzY3LjgxOSwxMDIuMTgxIDY5LjIyNywxMDMuMTM2IDY5LjA3LDEwNC4yMyBMMTQ4LjMyNyw1OC40NzEiIGlkPSJGaWxsLTQiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNjkuMDcsMTA0LjM0NyBDNjkuMDQ4LDEwNC4zNDcgNjkuMDI1LDEwNC4zNCA2OS4wMDUsMTA0LjMyNyBDNjguOTY4LDEwNC4zMDEgNjguOTQ4LDEwNC4yNTcgNjguOTU1LDEwNC4yMTMgQzY5LDEwMy44OTYgNjguODk4LDEwMy41NzYgNjguNjU4LDEwMy4yODggQzY4LjE1MywxMDIuNjc4IDY3LjEwMywxMDIuMjY2IDY1LjkyLDEwMi4yMTQgQzY1Ljc0MiwxMDIuMjA2IDY1LjU2MywxMDIuMjA3IDY1LjM4NSwxMDIuMjE1IEM2NC43NDIsMTAyLjI0NiA2NC4wODcsMTAyLjIzMiA2My40NSwxMDIuMTc0IEM2My4zOTksMTAyLjE2OSA2My4zNTgsMTAyLjEzMiA2My4zNDcsMTAyLjA4MiBDNjMuMzM2LDEwMi4wMzMgNjMuMzU4LDEwMS45ODEgNjMuNDAyLDEwMS45NTYgTDE0NC41MDYsNTUuMTM0IEMxNDQuNTM3LDU1LjExNiAxNDQuNTc1LDU1LjExMyAxNDQuNjA5LDU1LjEyNyBDMTQ0LjY0Miw1NS4xNDEgMTQ0LjY2OCw1NS4xNyAxNDQuNjc3LDU1LjIwNCBDMTQ0Ljc4MSw1NS41ODUgMTQ0LjgwNiw1NS45NzIgMTQ0Ljc1MSw1Ni4zNTcgQzE0NC43MDYsNTYuNjczIDE0NC44MDgsNTYuOTk0IDE0NS4wNDcsNTcuMjgyIEMxNDUuNTUzLDU3Ljg5MiAxNDYuNjAyLDU4LjMwMyAxNDcuNzg2LDU4LjM1NSBDMTQ3Ljk2NCw1OC4zNjMgMTQ4LjE0Myw1OC4zNjMgMTQ4LjMyMSw1OC4zNTQgQzE0OC4zNzcsNTguMzUyIDE0OC40MjQsNTguMzg3IDE0OC40MzksNTguNDM4IEMxNDguNDU0LDU4LjQ5IDE0OC40MzIsNTguNTQ1IDE0OC4zODUsNTguNTcyIEw2OS4xMjksMTA0LjMzMSBDNjkuMTExLDEwNC4zNDIgNjkuMDksMTA0LjM0NyA2OS4wNywxMDQuMzQ3IEw2OS4wNywxMDQuMzQ3IFogTTY1LjY2NSwxMDEuOTc1IEM2NS43NTQsMTAxLjk3NSA2NS44NDIsMTAxLjk3NyA2NS45MywxMDEuOTgxIEM2Ny4xOTYsMTAyLjAzNyA2OC4yODMsMTAyLjQ2OSA2OC44MzgsMTAzLjEzOSBDNjkuMDY1LDEwMy40MTMgNjkuMTg4LDEwMy43MTQgNjkuMTk4LDEwNC4wMjEgTDE0Ny44ODMsNTguNTkyIEMxNDcuODQ3LDU4LjU5MiAxNDcuODExLDU4LjU5MSAxNDcuNzc2LDU4LjU4OSBDMTQ2LjUwOSw1OC41MzMgMTQ1LjQyMiw1OC4xIDE0NC44NjcsNTcuNDMxIEMxNDQuNTg1LDU3LjA5MSAxNDQuNDY1LDU2LjcwNyAxNDQuNTIsNTYuMzI0IEMxNDQuNTYzLDU2LjAyMSAxNDQuNTUyLDU1LjcxNiAxNDQuNDg4LDU1LjQxNCBMNjMuODQ2LDEwMS45NyBDNjQuMzUzLDEwMi4wMDIgNjQuODY3LDEwMi4wMDYgNjUuMzc0LDEwMS45ODIgQzY1LjQ3MSwxMDEuOTc3IDY1LjU2OCwxMDEuOTc1IDY1LjY2NSwxMDEuOTc1IEw2NS42NjUsMTAxLjk3NSBaIiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTIuMjA4LDU1LjEzNCBDMS4yMDcsNTMuMzA3IDEuOTY3LDUwLjkxNyAzLjkwNiw0OS43OTcgTDU5LjkxNywxNy40NTMgQzYxLjg1NiwxNi4zMzMgNjQuMjQxLDE2LjkwNyA2NS4yNDMsMTguNzM0IEw2NS40NzUsMTkuMTQ0IEM2NS44NzIsMTkuODgyIDY2LjM2OCwyMC41NiA2Ni45NDUsMjEuMTY1IEw2Ny4yMjMsMjEuNDM1IEM3MC41NDgsMjQuNjQ5IDc1LjgwNiwyNS4xNTEgODAuMTExLDIyLjY2NSBMODcuNDMsMTguNDQ1IEM4OS4zNywxNy4zMjYgOTEuNzU0LDE3Ljg5OSA5Mi43NTUsMTkuNzI3IEw5Ni4wMDUsMjUuNjU1IEwxMi40ODYsNzMuODg0IEwyLjIwOCw1NS4xMzQgWiIgaWQ9IkZpbGwtNiIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMi40ODYsNzQuMDAxIEMxMi40NzYsNzQuMDAxIDEyLjQ2NSw3My45OTkgMTIuNDU1LDczLjk5NiBDMTIuNDI0LDczLjk4OCAxMi4zOTksNzMuOTY3IDEyLjM4NCw3My45NCBMMi4xMDYsNTUuMTkgQzEuMDc1LDUzLjMxIDEuODU3LDUwLjg0NSAzLjg0OCw0OS42OTYgTDU5Ljg1OCwxNy4zNTIgQzYwLjUyNSwxNi45NjcgNjEuMjcxLDE2Ljc2NCA2Mi4wMTYsMTYuNzY0IEM2My40MzEsMTYuNzY0IDY0LjY2NiwxNy40NjYgNjUuMzI3LDE4LjY0NiBDNjUuMzM3LDE4LjY1NCA2NS4zNDUsMTguNjYzIDY1LjM1MSwxOC42NzQgTDY1LjU3OCwxOS4wODggQzY1LjU4NCwxOS4xIDY1LjU4OSwxOS4xMTIgNjUuNTkxLDE5LjEyNiBDNjUuOTg1LDE5LjgzOCA2Ni40NjksMjAuNDk3IDY3LjAzLDIxLjA4NSBMNjcuMzA1LDIxLjM1MSBDNjkuMTUxLDIzLjEzNyA3MS42NDksMjQuMTIgNzQuMzM2LDI0LjEyIEM3Ni4zMTMsMjQuMTIgNzguMjksMjMuNTgyIDgwLjA1MywyMi41NjMgQzgwLjA2NCwyMi41NTcgODAuMDc2LDIyLjU1MyA4MC4wODgsMjIuNTUgTDg3LjM3MiwxOC4zNDQgQzg4LjAzOCwxNy45NTkgODguNzg0LDE3Ljc1NiA4OS41MjksMTcuNzU2IEM5MC45NTYsMTcuNzU2IDkyLjIwMSwxOC40NzIgOTIuODU4LDE5LjY3IEw5Ni4xMDcsMjUuNTk5IEM5Ni4xMzgsMjUuNjU0IDk2LjExOCwyNS43MjQgOTYuMDYzLDI1Ljc1NiBMMTIuNTQ1LDczLjk4NSBDMTIuNTI2LDczLjk5NiAxMi41MDYsNzQuMDAxIDEyLjQ4Niw3NC4wMDEgTDEyLjQ4Niw3NC4wMDEgWiBNNjIuMDE2LDE2Ljk5NyBDNjEuMzEyLDE2Ljk5NyA2MC42MDYsMTcuMTkgNTkuOTc1LDE3LjU1NCBMMy45NjUsNDkuODk5IEMyLjA4Myw1MC45ODUgMS4zNDEsNTMuMzA4IDIuMzEsNTUuMDc4IEwxMi41MzEsNzMuNzIzIEw5NS44NDgsMjUuNjExIEw5Mi42NTMsMTkuNzgyIEM5Mi4wMzgsMTguNjYgOTAuODcsMTcuOTkgODkuNTI5LDE3Ljk5IEM4OC44MjUsMTcuOTkgODguMTE5LDE4LjE4MiA4Ny40ODksMTguNTQ3IEw4MC4xNzIsMjIuNzcyIEM4MC4xNjEsMjIuNzc4IDgwLjE0OSwyMi43ODIgODAuMTM3LDIyLjc4NSBDNzguMzQ2LDIzLjgxMSA3Ni4zNDEsMjQuMzU0IDc0LjMzNiwyNC4zNTQgQzcxLjU4OCwyNC4zNTQgNjkuMDMzLDIzLjM0NyA2Ny4xNDIsMjEuNTE5IEw2Ni44NjQsMjEuMjQ5IEM2Ni4yNzcsMjAuNjM0IDY1Ljc3NCwxOS45NDcgNjUuMzY3LDE5LjIwMyBDNjUuMzYsMTkuMTkyIDY1LjM1NiwxOS4xNzkgNjUuMzU0LDE5LjE2NiBMNjUuMTYzLDE4LjgxOSBDNjUuMTU0LDE4LjgxMSA2NS4xNDYsMTguODAxIDY1LjE0LDE4Ljc5IEM2NC41MjUsMTcuNjY3IDYzLjM1NywxNi45OTcgNjIuMDE2LDE2Ljk5NyBMNjIuMDE2LDE2Ljk5NyBaIiBpZD0iRmlsbC03IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTQyLjQzNCw0OC44MDggTDQyLjQzNCw0OC44MDggQzM5LjkyNCw0OC44MDcgMzcuNzM3LDQ3LjU1IDM2LjU4Miw0NS40NDMgQzM0Ljc3MSw0Mi4xMzkgMzYuMTQ0LDM3LjgwOSAzOS42NDEsMzUuNzg5IEw1MS45MzIsMjguNjkxIEM1My4xMDMsMjguMDE1IDU0LjQxMywyNy42NTggNTUuNzIxLDI3LjY1OCBDNTguMjMxLDI3LjY1OCA2MC40MTgsMjguOTE2IDYxLjU3MywzMS4wMjMgQzYzLjM4NCwzNC4zMjcgNjIuMDEyLDM4LjY1NyA1OC41MTQsNDAuNjc3IEw0Ni4yMjMsNDcuNzc1IEM0NS4wNTMsNDguNDUgNDMuNzQyLDQ4LjgwOCA0Mi40MzQsNDguODA4IEw0Mi40MzQsNDguODA4IFogTTU1LjcyMSwyOC4xMjUgQzU0LjQ5NSwyOC4xMjUgNTMuMjY1LDI4LjQ2MSA1Mi4xNjYsMjkuMDk2IEwzOS44NzUsMzYuMTk0IEMzNi41OTYsMzguMDg3IDM1LjMwMiw0Mi4xMzYgMzYuOTkyLDQ1LjIxOCBDMzguMDYzLDQ3LjE3MyA0MC4wOTgsNDguMzQgNDIuNDM0LDQ4LjM0IEM0My42NjEsNDguMzQgNDQuODksNDguMDA1IDQ1Ljk5LDQ3LjM3IEw1OC4yODEsNDAuMjcyIEM2MS41NiwzOC4zNzkgNjIuODUzLDM0LjMzIDYxLjE2NCwzMS4yNDggQzYwLjA5MiwyOS4yOTMgNTguMDU4LDI4LjEyNSA1NS43MjEsMjguMTI1IEw1NS43MjEsMjguMTI1IFoiIGlkPSJGaWxsLTgiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjU4OCwyLjQwNyBDMTQ5LjU4OCwyLjQwNyAxNTUuNzY4LDUuOTc1IDE1Ni4zMjUsNi4yOTcgTDE1Ni4zMjUsNy4xODQgQzE1Ni4zMjUsNy4zNiAxNTYuMzM4LDcuNTQ0IDE1Ni4zNjIsNy43MzMgQzE1Ni4zNzMsNy44MTQgMTU2LjM4Miw3Ljg5NCAxNTYuMzksNy45NzUgQzE1Ni41Myw5LjM5IDE1Ny4zNjMsMTAuOTczIDE1OC40OTUsMTEuOTc0IEwxNjUuODkxLDE4LjUxOSBDMTY2LjA2OCwxOC42NzUgMTY2LjI0OSwxOC44MTQgMTY2LjQzMiwxOC45MzQgQzE2OC4wMTEsMTkuOTc0IDE2OS4zODIsMTkuNCAxNjkuNDk0LDE3LjY1MiBDMTY5LjU0MywxNi44NjggMTY5LjU1MSwxNi4wNTcgMTY5LjUxNywxNS4yMjMgTDE2OS41MTQsMTUuMDYzIEwxNjkuNTE0LDEzLjkxMiBDMTcwLjc4LDE0LjY0MiAxOTUuNTAxLDI4LjkxNSAxOTUuNTAxLDI4LjkxNSBMMTk1LjUwMSw4Mi45MTUgQzE5NS41MDEsODQuMDA1IDE5NC43MzEsODQuNDQ1IDE5My43ODEsODMuODk3IEwxNTEuMzA4LDU5LjM3NCBDMTUwLjM1OCw1OC44MjYgMTQ5LjU4OCw1Ny40OTcgMTQ5LjU4OCw1Ni40MDggTDE0OS41ODgsMjIuMzc1IiBpZD0iRmlsbC05IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE5NC41NTMsODQuMjUgQzE5NC4yOTYsODQuMjUgMTk0LjAxMyw4NC4xNjUgMTkzLjcyMiw4My45OTcgTDE1MS4yNSw1OS40NzYgQzE1MC4yNjksNTguOTA5IDE0OS40NzEsNTcuNTMzIDE0OS40NzEsNTYuNDA4IEwxNDkuNDcxLDIyLjM3NSBMMTQ5LjcwNSwyMi4zNzUgTDE0OS43MDUsNTYuNDA4IEMxNDkuNzA1LDU3LjQ1OSAxNTAuNDUsNTguNzQ0IDE1MS4zNjYsNTkuMjc0IEwxOTMuODM5LDgzLjc5NSBDMTk0LjI2Myw4NC4wNCAxOTQuNjU1LDg0LjA4MyAxOTQuOTQyLDgzLjkxNyBDMTk1LjIyNyw4My43NTMgMTk1LjM4NCw4My4zOTcgMTk1LjM4NCw4Mi45MTUgTDE5NS4zODQsMjguOTgyIEMxOTQuMTAyLDI4LjI0MiAxNzIuMTA0LDE1LjU0MiAxNjkuNjMxLDE0LjExNCBMMTY5LjYzNCwxNS4yMiBDMTY5LjY2OCwxNi4wNTIgMTY5LjY2LDE2Ljg3NCAxNjkuNjEsMTcuNjU5IEMxNjkuNTU2LDE4LjUwMyAxNjkuMjE0LDE5LjEyMyAxNjguNjQ3LDE5LjQwNSBDMTY4LjAyOCwxOS43MTQgMTY3LjE5NywxOS41NzggMTY2LjM2NywxOS4wMzIgQzE2Ni4xODEsMTguOTA5IDE2NS45OTUsMTguNzY2IDE2NS44MTQsMTguNjA2IEwxNTguNDE3LDEyLjA2MiBDMTU3LjI1OSwxMS4wMzYgMTU2LjQxOCw5LjQzNyAxNTYuMjc0LDcuOTg2IEMxNTYuMjY2LDcuOTA3IDE1Ni4yNTcsNy44MjcgMTU2LjI0Nyw3Ljc0OCBDMTU2LjIyMSw3LjU1NSAxNTYuMjA5LDcuMzY1IDE1Ni4yMDksNy4xODQgTDE1Ni4yMDksNi4zNjQgQzE1NS4zNzUsNS44ODMgMTQ5LjUyOSwyLjUwOCAxNDkuNTI5LDIuNTA4IEwxNDkuNjQ2LDIuMzA2IEMxNDkuNjQ2LDIuMzA2IDE1NS44MjcsNS44NzQgMTU2LjM4NCw2LjE5NiBMMTU2LjQ0Miw2LjIzIEwxNTYuNDQyLDcuMTg0IEMxNTYuNDQyLDcuMzU1IDE1Ni40NTQsNy41MzUgMTU2LjQ3OCw3LjcxNyBDMTU2LjQ4OSw3LjggMTU2LjQ5OSw3Ljg4MiAxNTYuNTA3LDcuOTYzIEMxNTYuNjQ1LDkuMzU4IDE1Ny40NTUsMTAuODk4IDE1OC41NzIsMTEuODg2IEwxNjUuOTY5LDE4LjQzMSBDMTY2LjE0MiwxOC41ODQgMTY2LjMxOSwxOC43MiAxNjYuNDk2LDE4LjgzNyBDMTY3LjI1NCwxOS4zMzYgMTY4LDE5LjQ2NyAxNjguNTQzLDE5LjE5NiBDMTY5LjAzMywxOC45NTMgMTY5LjMyOSwxOC40MDEgMTY5LjM3NywxNy42NDUgQzE2OS40MjcsMTYuODY3IDE2OS40MzQsMTYuMDU0IDE2OS40MDEsMTUuMjI4IEwxNjkuMzk3LDE1LjA2NSBMMTY5LjM5NywxMy43MSBMMTY5LjU3MiwxMy44MSBDMTcwLjgzOSwxNC41NDEgMTk1LjU1OSwyOC44MTQgMTk1LjU1OSwyOC44MTQgTDE5NS42MTgsMjguODQ3IEwxOTUuNjE4LDgyLjkxNSBDMTk1LjYxOCw4My40ODQgMTk1LjQyLDgzLjkxMSAxOTUuMDU5LDg0LjExOSBDMTk0LjkwOCw4NC4yMDYgMTk0LjczNyw4NC4yNSAxOTQuNTUzLDg0LjI1IiBpZD0iRmlsbC0xMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDUuNjg1LDU2LjE2MSBMMTY5LjgsNzAuMDgzIEwxNDMuODIyLDg1LjA4MSBMMTQyLjM2LDg0Ljc3NCBDMTM1LjgyNiw4Mi42MDQgMTI4LjczMiw4MS4wNDYgMTIxLjM0MSw4MC4xNTggQzExNi45NzYsNzkuNjM0IDExMi42NzgsODEuMjU0IDExMS43NDMsODMuNzc4IEMxMTEuNTA2LDg0LjQxNCAxMTEuNTAzLDg1LjA3MSAxMTEuNzMyLDg1LjcwNiBDMTEzLjI3LDg5Ljk3MyAxMTUuOTY4LDk0LjA2OSAxMTkuNzI3LDk3Ljg0MSBMMTIwLjI1OSw5OC42ODYgQzEyMC4yNiw5OC42ODUgOTQuMjgyLDExMy42ODMgOTQuMjgyLDExMy42ODMgTDcwLjE2Nyw5OS43NjEgTDE0NS42ODUsNTYuMTYxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik05NC4yODIsMTEzLjgxOCBMOTQuMjIzLDExMy43ODUgTDY5LjkzMyw5OS43NjEgTDcwLjEwOCw5OS42NiBMMTQ1LjY4NSw1Ni4wMjYgTDE0NS43NDMsNTYuMDU5IEwxNzAuMDMzLDcwLjA4MyBMMTQzLjg0Miw4NS4yMDUgTDE0My43OTcsODUuMTk1IEMxNDMuNzcyLDg1LjE5IDE0Mi4zMzYsODQuODg4IDE0Mi4zMzYsODQuODg4IEMxMzUuNzg3LDgyLjcxNCAxMjguNzIzLDgxLjE2MyAxMjEuMzI3LDgwLjI3NCBDMTIwLjc4OCw4MC4yMDkgMTIwLjIzNiw4MC4xNzcgMTE5LjY4OSw4MC4xNzcgQzExNS45MzEsODAuMTc3IDExMi42MzUsODEuNzA4IDExMS44NTIsODMuODE5IEMxMTEuNjI0LDg0LjQzMiAxMTEuNjIxLDg1LjA1MyAxMTEuODQyLDg1LjY2NyBDMTEzLjM3Nyw4OS45MjUgMTE2LjA1OCw5My45OTMgMTE5LjgxLDk3Ljc1OCBMMTE5LjgyNiw5Ny43NzkgTDEyMC4zNTIsOTguNjE0IEMxMjAuMzU0LDk4LjYxNyAxMjAuMzU2LDk4LjYyIDEyMC4zNTgsOTguNjI0IEwxMjAuNDIyLDk4LjcyNiBMMTIwLjMxNyw5OC43ODcgQzEyMC4yNjQsOTguODE4IDk0LjU5OSwxMTMuNjM1IDk0LjM0LDExMy43ODUgTDk0LjI4MiwxMTMuODE4IEw5NC4yODIsMTEzLjgxOCBaIE03MC40MDEsOTkuNzYxIEw5NC4yODIsMTEzLjU0OSBMMTE5LjA4NCw5OS4yMjkgQzExOS42Myw5OC45MTQgMTE5LjkzLDk4Ljc0IDEyMC4xMDEsOTguNjU0IEwxMTkuNjM1LDk3LjkxNCBDMTE1Ljg2NCw5NC4xMjcgMTEzLjE2OCw5MC4wMzMgMTExLjYyMiw4NS43NDYgQzExMS4zODIsODUuMDc5IDExMS4zODYsODQuNDA0IDExMS42MzMsODMuNzM4IEMxMTIuNDQ4LDgxLjUzOSAxMTUuODM2LDc5Ljk0MyAxMTkuNjg5LDc5Ljk0MyBDMTIwLjI0Niw3OS45NDMgMTIwLjgwNiw3OS45NzYgMTIxLjM1NSw4MC4wNDIgQzEyOC43NjcsODAuOTMzIDEzNS44NDYsODIuNDg3IDE0Mi4zOTYsODQuNjYzIEMxNDMuMjMyLDg0LjgzOCAxNDMuNjExLDg0LjkxNyAxNDMuNzg2LDg0Ljk2NyBMMTY5LjU2Niw3MC4wODMgTDE0NS42ODUsNTYuMjk1IEw3MC40MDEsOTkuNzYxIEw3MC40MDEsOTkuNzYxIFoiIGlkPSJGaWxsLTEyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2Ny4yMywxOC45NzkgTDE2Ny4yMyw2OS44NSBMMTM5LjkwOSw4NS42MjMgTDEzMy40NDgsNzEuNDU2IEMxMzIuNTM4LDY5LjQ2IDEzMC4wMiw2OS43MTggMTI3LjgyNCw3Mi4wMyBDMTI2Ljc2OSw3My4xNCAxMjUuOTMxLDc0LjU4NSAxMjUuNDk0LDc2LjA0OCBMMTE5LjAzNCw5Ny42NzYgTDkxLjcxMiwxMTMuNDUgTDkxLjcxMiw2Mi41NzkgTDE2Ny4yMywxOC45NzkiIGlkPSJGaWxsLTEzIiBmaWxsPSIjRkZGRkZGIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTkxLjcxMiwxMTMuNTY3IEM5MS42OTIsMTEzLjU2NyA5MS42NzIsMTEzLjU2MSA5MS42NTMsMTEzLjU1MSBDOTEuNjE4LDExMy41MyA5MS41OTUsMTEzLjQ5MiA5MS41OTUsMTEzLjQ1IEw5MS41OTUsNjIuNTc5IEM5MS41OTUsNjIuNTM3IDkxLjYxOCw2Mi40OTkgOTEuNjUzLDYyLjQ3OCBMMTY3LjE3MiwxOC44NzggQzE2Ny4yMDgsMTguODU3IDE2Ny4yNTIsMTguODU3IDE2Ny4yODgsMTguODc4IEMxNjcuMzI0LDE4Ljg5OSAxNjcuMzQ3LDE4LjkzNyAxNjcuMzQ3LDE4Ljk3OSBMMTY3LjM0Nyw2OS44NSBDMTY3LjM0Nyw2OS44OTEgMTY3LjMyNCw2OS45MyAxNjcuMjg4LDY5Ljk1IEwxMzkuOTY3LDg1LjcyNSBDMTM5LjkzOSw4NS43NDEgMTM5LjkwNSw4NS43NDUgMTM5Ljg3Myw4NS43MzUgQzEzOS44NDIsODUuNzI1IDEzOS44MTYsODUuNzAyIDEzOS44MDIsODUuNjcyIEwxMzMuMzQyLDcxLjUwNCBDMTMyLjk2Nyw3MC42ODIgMTMyLjI4LDcwLjIyOSAxMzEuNDA4LDcwLjIyOSBDMTMwLjMxOSw3MC4yMjkgMTI5LjA0NCw3MC45MTUgMTI3LjkwOCw3Mi4xMSBDMTI2Ljg3NCw3My4yIDEyNi4wMzQsNzQuNjQ3IDEyNS42MDYsNzYuMDgyIEwxMTkuMTQ2LDk3LjcwOSBDMTE5LjEzNyw5Ny43MzggMTE5LjExOCw5Ny43NjIgMTE5LjA5Miw5Ny43NzcgTDkxLjc3LDExMy41NTEgQzkxLjc1MiwxMTMuNTYxIDkxLjczMiwxMTMuNTY3IDkxLjcxMiwxMTMuNTY3IEw5MS43MTIsMTEzLjU2NyBaIE05MS44MjksNjIuNjQ3IEw5MS44MjksMTEzLjI0OCBMMTE4LjkzNSw5Ny41OTggTDEyNS4zODIsNzYuMDE1IEMxMjUuODI3LDc0LjUyNSAxMjYuNjY0LDczLjA4MSAxMjcuNzM5LDcxLjk1IEMxMjguOTE5LDcwLjcwOCAxMzAuMjU2LDY5Ljk5NiAxMzEuNDA4LDY5Ljk5NiBDMTMyLjM3Nyw2OS45OTYgMTMzLjEzOSw3MC40OTcgMTMzLjU1NCw3MS40MDcgTDEzOS45NjEsODUuNDU4IEwxNjcuMTEzLDY5Ljc4MiBMMTY3LjExMywxOS4xODEgTDkxLjgyOSw2Mi42NDcgTDkxLjgyOSw2Mi42NDcgWiIgaWQ9IkZpbGwtMTQiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTY4LjU0MywxOS4yMTMgTDE2OC41NDMsNzAuMDgzIEwxNDEuMjIxLDg1Ljg1NyBMMTM0Ljc2MSw3MS42ODkgQzEzMy44NTEsNjkuNjk0IDEzMS4zMzMsNjkuOTUxIDEyOS4xMzcsNzIuMjYzIEMxMjguMDgyLDczLjM3NCAxMjcuMjQ0LDc0LjgxOSAxMjYuODA3LDc2LjI4MiBMMTIwLjM0Niw5Ny45MDkgTDkzLjAyNSwxMTMuNjgzIEw5My4wMjUsNjIuODEzIEwxNjguNTQzLDE5LjIxMyIgaWQ9IkZpbGwtMTUiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTMuMDI1LDExMy44IEM5My4wMDUsMTEzLjggOTIuOTg0LDExMy43OTUgOTIuOTY2LDExMy43ODUgQzkyLjkzMSwxMTMuNzY0IDkyLjkwOCwxMTMuNzI1IDkyLjkwOCwxMTMuNjg0IEw5Mi45MDgsNjIuODEzIEM5Mi45MDgsNjIuNzcxIDkyLjkzMSw2Mi43MzMgOTIuOTY2LDYyLjcxMiBMMTY4LjQ4NCwxOS4xMTIgQzE2OC41MiwxOS4wOSAxNjguNTY1LDE5LjA5IDE2OC42MDEsMTkuMTEyIEMxNjguNjM3LDE5LjEzMiAxNjguNjYsMTkuMTcxIDE2OC42NiwxOS4yMTIgTDE2OC42Niw3MC4wODMgQzE2OC42Niw3MC4xMjUgMTY4LjYzNyw3MC4xNjQgMTY4LjYwMSw3MC4xODQgTDE0MS4yOCw4NS45NTggQzE0MS4yNTEsODUuOTc1IDE0MS4yMTcsODUuOTc5IDE0MS4xODYsODUuOTY4IEMxNDEuMTU0LDg1Ljk1OCAxNDEuMTI5LDg1LjkzNiAxNDEuMTE1LDg1LjkwNiBMMTM0LjY1NSw3MS43MzggQzEzNC4yOCw3MC45MTUgMTMzLjU5Myw3MC40NjMgMTMyLjcyLDcwLjQ2MyBDMTMxLjYzMiw3MC40NjMgMTMwLjM1Nyw3MS4xNDggMTI5LjIyMSw3Mi4zNDQgQzEyOC4xODYsNzMuNDMzIDEyNy4zNDcsNzQuODgxIDEyNi45MTksNzYuMzE1IEwxMjAuNDU4LDk3Ljk0MyBDMTIwLjQ1LDk3Ljk3MiAxMjAuNDMxLDk3Ljk5NiAxMjAuNDA1LDk4LjAxIEw5My4wODMsMTEzLjc4NSBDOTMuMDY1LDExMy43OTUgOTMuMDQ1LDExMy44IDkzLjAyNSwxMTMuOCBMOTMuMDI1LDExMy44IFogTTkzLjE0Miw2Mi44ODEgTDkzLjE0MiwxMTMuNDgxIEwxMjAuMjQ4LDk3LjgzMiBMMTI2LjY5NSw3Ni4yNDggQzEyNy4xNCw3NC43NTggMTI3Ljk3Nyw3My4zMTUgMTI5LjA1Miw3Mi4xODMgQzEzMC4yMzEsNzAuOTQyIDEzMS41NjgsNzAuMjI5IDEzMi43Miw3MC4yMjkgQzEzMy42ODksNzAuMjI5IDEzNC40NTIsNzAuNzMxIDEzNC44NjcsNzEuNjQxIEwxNDEuMjc0LDg1LjY5MiBMMTY4LjQyNiw3MC4wMTYgTDE2OC40MjYsMTkuNDE1IEw5My4xNDIsNjIuODgxIEw5My4xNDIsNjIuODgxIFoiIGlkPSJGaWxsLTE2IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS44LDcwLjA4MyBMMTQyLjQ3OCw4NS44NTcgTDEzNi4wMTgsNzEuNjg5IEMxMzUuMTA4LDY5LjY5NCAxMzIuNTksNjkuOTUxIDEzMC4zOTMsNzIuMjYzIEMxMjkuMzM5LDczLjM3NCAxMjguNSw3NC44MTkgMTI4LjA2NCw3Ni4yODIgTDEyMS42MDMsOTcuOTA5IEw5NC4yODIsMTEzLjY4MyBMOTQuMjgyLDYyLjgxMyBMMTY5LjgsMTkuMjEzIEwxNjkuOCw3MC4wODMgWiIgaWQ9IkZpbGwtMTciIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTQuMjgyLDExMy45MTcgQzk0LjI0MSwxMTMuOTE3IDk0LjIwMSwxMTMuOTA3IDk0LjE2NSwxMTMuODg2IEM5NC4wOTMsMTEzLjg0NSA5NC4wNDgsMTEzLjc2NyA5NC4wNDgsMTEzLjY4NCBMOTQuMDQ4LDYyLjgxMyBDOTQuMDQ4LDYyLjczIDk0LjA5Myw2Mi42NTIgOTQuMTY1LDYyLjYxMSBMMTY5LjY4MywxOS4wMSBDMTY5Ljc1NSwxOC45NjkgMTY5Ljg0NCwxOC45NjkgMTY5LjkxNywxOS4wMSBDMTY5Ljk4OSwxOS4wNTIgMTcwLjAzMywxOS4xMjkgMTcwLjAzMywxOS4yMTIgTDE3MC4wMzMsNzAuMDgzIEMxNzAuMDMzLDcwLjE2NiAxNjkuOTg5LDcwLjI0NCAxNjkuOTE3LDcwLjI4NSBMMTQyLjU5NSw4Ni4wNiBDMTQyLjUzOCw4Ni4wOTIgMTQyLjQ2OSw4Ni4xIDE0Mi40MDcsODYuMDggQzE0Mi4zNDQsODYuMDYgMTQyLjI5Myw4Ni4wMTQgMTQyLjI2Niw4NS45NTQgTDEzNS44MDUsNzEuNzg2IEMxMzUuNDQ1LDcwLjk5NyAxMzQuODEzLDcwLjU4IDEzMy45NzcsNzAuNTggQzEzMi45MjEsNzAuNTggMTMxLjY3Niw3MS4yNTIgMTMwLjU2Miw3Mi40MjQgQzEyOS41NCw3My41MDEgMTI4LjcxMSw3NC45MzEgMTI4LjI4Nyw3Ni4zNDggTDEyMS44MjcsOTcuOTc2IEMxMjEuODEsOTguMDM0IDEyMS43NzEsOTguMDgyIDEyMS43Miw5OC4xMTIgTDk0LjM5OCwxMTMuODg2IEM5NC4zNjIsMTEzLjkwNyA5NC4zMjIsMTEzLjkxNyA5NC4yODIsMTEzLjkxNyBMOTQuMjgyLDExMy45MTcgWiBNOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDExMy4yNzkgTDEyMS40MDYsOTcuNzU0IEwxMjcuODQsNzYuMjE1IEMxMjguMjksNzQuNzA4IDEyOS4xMzcsNzMuMjQ3IDEzMC4yMjQsNzIuMTAzIEMxMzEuNDI1LDcwLjgzOCAxMzIuNzkzLDcwLjExMiAxMzMuOTc3LDcwLjExMiBDMTM0Ljk5NSw3MC4xMTIgMTM1Ljc5NSw3MC42MzggMTM2LjIzLDcxLjU5MiBMMTQyLjU4NCw4NS41MjYgTDE2OS41NjYsNjkuOTQ4IEwxNjkuNTY2LDE5LjYxNyBMOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDYyLjk0OCBaIiBpZD0iRmlsbC0xOCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMDkuODk0LDkyLjk0MyBMMTA5Ljg5NCw5Mi45NDMgQzEwOC4xMiw5Mi45NDMgMTA2LjY1Myw5Mi4yMTggMTA1LjY1LDkwLjgyMyBDMTA1LjU4Myw5MC43MzEgMTA1LjU5Myw5MC42MSAxMDUuNjczLDkwLjUyOSBDMTA1Ljc1Myw5MC40NDggMTA1Ljg4LDkwLjQ0IDEwNS45NzQsOTAuNTA2IEMxMDYuNzU0LDkxLjA1MyAxMDcuNjc5LDkxLjMzMyAxMDguNzI0LDkxLjMzMyBDMTEwLjA0Nyw5MS4zMzMgMTExLjQ3OCw5MC44OTQgMTEyLjk4LDkwLjAyNyBDMTE4LjI5MSw4Ni45NiAxMjIuNjExLDc5LjUwOSAxMjIuNjExLDczLjQxNiBDMTIyLjYxMSw3MS40ODkgMTIyLjE2OSw2OS44NTYgMTIxLjMzMyw2OC42OTIgQzEyMS4yNjYsNjguNiAxMjEuMjc2LDY4LjQ3MyAxMjEuMzU2LDY4LjM5MiBDMTIxLjQzNiw2OC4zMTEgMTIxLjU2Myw2OC4yOTkgMTIxLjY1Niw2OC4zNjUgQzEyMy4zMjcsNjkuNTM3IDEyNC4yNDcsNzEuNzQ2IDEyNC4yNDcsNzQuNTg0IEMxMjQuMjQ3LDgwLjgyNiAxMTkuODIxLDg4LjQ0NyAxMTQuMzgyLDkxLjU4NyBDMTEyLjgwOCw5Mi40OTUgMTExLjI5OCw5Mi45NDMgMTA5Ljg5NCw5Mi45NDMgTDEwOS44OTQsOTIuOTQzIFogTTEwNi45MjUsOTEuNDAxIEMxMDcuNzM4LDkyLjA1MiAxMDguNzQ1LDkyLjI3OCAxMDkuODkzLDkyLjI3OCBMMTA5Ljg5NCw5Mi4yNzggQzExMS4yMTUsOTIuMjc4IDExMi42NDcsOTEuOTUxIDExNC4xNDgsOTEuMDg0IEMxMTkuNDU5LDg4LjAxNyAxMjMuNzgsODAuNjIxIDEyMy43OCw3NC41MjggQzEyMy43OCw3Mi41NDkgMTIzLjMxNyw3MC45MjkgMTIyLjQ1NCw2OS43NjcgQzEyMi44NjUsNzAuODAyIDEyMy4wNzksNzIuMDQyIDEyMy4wNzksNzMuNDAyIEMxMjMuMDc5LDc5LjY0NSAxMTguNjUzLDg3LjI4NSAxMTMuMjE0LDkwLjQyNSBDMTExLjY0LDkxLjMzNCAxMTAuMTMsOTEuNzQyIDEwOC43MjQsOTEuNzQyIEMxMDguMDgzLDkxLjc0MiAxMDcuNDgxLDkxLjU5MyAxMDYuOTI1LDkxLjQwMSBMMTA2LjkyNSw5MS40MDEgWiIgaWQ9IkZpbGwtMTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjA5Nyw5MC4yMyBDMTE4LjQ4MSw4Ny4xMjIgMTIyLjg0NSw3OS41OTQgMTIyLjg0NSw3My40MTYgQzEyMi44NDUsNzEuMzY1IDEyMi4zNjIsNjkuNzI0IDEyMS41MjIsNjguNTU2IEMxMTkuNzM4LDY3LjMwNCAxMTcuMTQ4LDY3LjM2MiAxMTQuMjY1LDY5LjAyNiBDMTA4Ljg4MSw3Mi4xMzQgMTA0LjUxNyw3OS42NjIgMTA0LjUxNyw4NS44NCBDMTA0LjUxNyw4Ny44OTEgMTA1LDg5LjUzMiAxMDUuODQsOTAuNyBDMTA3LjYyNCw5MS45NTIgMTEwLjIxNCw5MS44OTQgMTEzLjA5Nyw5MC4yMyIgaWQ9IkZpbGwtMjAiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTA4LjcyNCw5MS42MTQgTDEwOC43MjQsOTEuNjE0IEMxMDcuNTgyLDkxLjYxNCAxMDYuNTY2LDkxLjQwMSAxMDUuNzA1LDkwLjc5NyBDMTA1LjY4NCw5MC43ODMgMTA1LjY2NSw5MC44MTEgMTA1LjY1LDkwLjc5IEMxMDQuNzU2LDg5LjU0NiAxMDQuMjgzLDg3Ljg0MiAxMDQuMjgzLDg1LjgxNyBDMTA0LjI4Myw3OS41NzUgMTA4LjcwOSw3MS45NTMgMTE0LjE0OCw2OC44MTIgQzExNS43MjIsNjcuOTA0IDExNy4yMzIsNjcuNDQ5IDExOC42MzgsNjcuNDQ5IEMxMTkuNzgsNjcuNDQ5IDEyMC43OTYsNjcuNzU4IDEyMS42NTYsNjguMzYyIEMxMjEuNjc4LDY4LjM3NyAxMjEuNjk3LDY4LjM5NyAxMjEuNzEyLDY4LjQxOCBDMTIyLjYwNiw2OS42NjIgMTIzLjA3OSw3MS4zOSAxMjMuMDc5LDczLjQxNSBDMTIzLjA3OSw3OS42NTggMTE4LjY1Myw4Ny4xOTggMTEzLjIxNCw5MC4zMzggQzExMS42NCw5MS4yNDcgMTEwLjEzLDkxLjYxNCAxMDguNzI0LDkxLjYxNCBMMTA4LjcyNCw5MS42MTQgWiBNMTA2LjAwNiw5MC41MDUgQzEwNi43OCw5MS4wMzcgMTA3LjY5NCw5MS4yODEgMTA4LjcyNCw5MS4yODEgQzExMC4wNDcsOTEuMjgxIDExMS40NzgsOTAuODY4IDExMi45OCw5MC4wMDEgQzExOC4yOTEsODYuOTM1IDEyMi42MTEsNzkuNDk2IDEyMi42MTEsNzMuNDAzIEMxMjIuNjExLDcxLjQ5NCAxMjIuMTc3LDY5Ljg4IDEyMS4zNTYsNjguNzE4IEMxMjAuNTgyLDY4LjE4NSAxMTkuNjY4LDY3LjkxOSAxMTguNjM4LDY3LjkxOSBDMTE3LjMxNSw2Ny45MTkgMTE1Ljg4Myw2OC4zNiAxMTQuMzgyLDY5LjIyNyBDMTA5LjA3MSw3Mi4yOTMgMTA0Ljc1MSw3OS43MzMgMTA0Ljc1MSw4NS44MjYgQzEwNC43NTEsODcuNzM1IDEwNS4xODUsODkuMzQzIDEwNi4wMDYsOTAuNTA1IEwxMDYuMDA2LDkwLjUwNSBaIiBpZD0iRmlsbC0yMSIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDkuMzE4LDcuMjYyIEwxMzkuMzM0LDE2LjE0IEwxNTUuMjI3LDI3LjE3MSBMMTYwLjgxNiwyMS4wNTkgTDE0OS4zMTgsNy4yNjIiIGlkPSJGaWxsLTIyIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS42NzYsMTMuODQgTDE1OS45MjgsMTkuNDY3IEMxNTYuMjg2LDIxLjU3IDE1MC40LDIxLjU4IDE0Ni43ODEsMTkuNDkxIEMxNDMuMTYxLDE3LjQwMiAxNDMuMTgsMTQuMDAzIDE0Ni44MjIsMTEuOSBMMTU2LjMxNyw2LjI5MiBMMTQ5LjU4OCwyLjQwNyBMNjcuNzUyLDQ5LjQ3OCBMMTEzLjY3NSw3NS45OTIgTDExNi43NTYsNzQuMjEzIEMxMTcuMzg3LDczLjg0OCAxMTcuNjI1LDczLjMxNSAxMTcuMzc0LDcyLjgyMyBDMTE1LjAxNyw2OC4xOTEgMTE0Ljc4MSw2My4yNzcgMTE2LjY5MSw1OC41NjEgQzEyMi4zMjksNDQuNjQxIDE0MS4yLDMzLjc0NiAxNjUuMzA5LDMwLjQ5MSBDMTczLjQ3OCwyOS4zODggMTgxLjk4OSwyOS41MjQgMTkwLjAxMywzMC44ODUgQzE5MC44NjUsMzEuMDMgMTkxLjc4OSwzMC44OTMgMTkyLjQyLDMwLjUyOCBMMTk1LjUwMSwyOC43NSBMMTY5LjY3NiwxMy44NCIgaWQ9IkZpbGwtMjMiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3Ni40NTkgQzExMy41OTQsNzYuNDU5IDExMy41MTQsNzYuNDM4IDExMy40NDIsNzYuMzk3IEw2Ny41MTgsNDkuODgyIEM2Ny4zNzQsNDkuNzk5IDY3LjI4NCw0OS42NDUgNjcuMjg1LDQ5LjQ3OCBDNjcuMjg1LDQ5LjMxMSA2Ny4zNzQsNDkuMTU3IDY3LjUxOSw0OS4wNzMgTDE0OS4zNTUsMi4wMDIgQzE0OS40OTksMS45MTkgMTQ5LjY3NywxLjkxOSAxNDkuODIxLDIuMDAyIEwxNTYuNTUsNS44ODcgQzE1Ni43NzQsNi4wMTcgMTU2Ljg1LDYuMzAyIDE1Ni43MjIsNi41MjYgQzE1Ni41OTIsNi43NDkgMTU2LjMwNyw2LjgyNiAxNTYuMDgzLDYuNjk2IEwxNDkuNTg3LDIuOTQ2IEw2OC42ODcsNDkuNDc5IEwxMTMuNjc1LDc1LjQ1MiBMMTE2LjUyMyw3My44MDggQzExNi43MTUsNzMuNjk3IDExNy4xNDMsNzMuMzk5IDExNi45NTgsNzMuMDM1IEMxMTQuNTQyLDY4LjI4NyAxMTQuMyw2My4yMjEgMTE2LjI1OCw1OC4zODUgQzExOS4wNjQsNTEuNDU4IDEyNS4xNDMsNDUuMTQzIDEzMy44NCw0MC4xMjIgQzE0Mi40OTcsMzUuMTI0IDE1My4zNTgsMzEuNjMzIDE2NS4yNDcsMzAuMDI4IEMxNzMuNDQ1LDI4LjkyMSAxODIuMDM3LDI5LjA1OCAxOTAuMDkxLDMwLjQyNSBDMTkwLjgzLDMwLjU1IDE5MS42NTIsMzAuNDMyIDE5Mi4xODYsMzAuMTI0IEwxOTQuNTY3LDI4Ljc1IEwxNjkuNDQyLDE0LjI0NCBDMTY5LjIxOSwxNC4xMTUgMTY5LjE0MiwxMy44MjkgMTY5LjI3MSwxMy42MDYgQzE2OS40LDEzLjM4MiAxNjkuNjg1LDEzLjMwNiAxNjkuOTA5LDEzLjQzNSBMMTk1LjczNCwyOC4zNDUgQzE5NS44NzksMjguNDI4IDE5NS45NjgsMjguNTgzIDE5NS45NjgsMjguNzUgQzE5NS45NjgsMjguOTE2IDE5NS44NzksMjkuMDcxIDE5NS43MzQsMjkuMTU0IEwxOTIuNjUzLDMwLjkzMyBDMTkxLjkzMiwzMS4zNSAxOTAuODksMzEuNTA4IDE4OS45MzUsMzEuMzQ2IEMxODEuOTcyLDI5Ljk5NSAxNzMuNDc4LDI5Ljg2IDE2NS4zNzIsMzAuOTU0IEMxNTMuNjAyLDMyLjU0MyAxNDIuODYsMzUuOTkzIDEzNC4zMDcsNDAuOTMxIEMxMjUuNzkzLDQ1Ljg0NyAxMTkuODUxLDUyLjAwNCAxMTcuMTI0LDU4LjczNiBDMTE1LjI3LDYzLjMxNCAxMTUuNTAxLDY4LjExMiAxMTcuNzksNzIuNjExIEMxMTguMTYsNzMuMzM2IDExNy44NDUsNzQuMTI0IDExNi45OSw3NC42MTcgTDExMy45MDksNzYuMzk3IEMxMTMuODM2LDc2LjQzOCAxMTMuNzU2LDc2LjQ1OSAxMTMuNjc1LDc2LjQ1OSIgaWQ9IkZpbGwtMjQiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUzLjMxNiwyMS4yNzkgQzE1MC45MDMsMjEuMjc5IDE0OC40OTUsMjAuNzUxIDE0Ni42NjQsMTkuNjkzIEMxNDQuODQ2LDE4LjY0NCAxNDMuODQ0LDE3LjIzMiAxNDMuODQ0LDE1LjcxOCBDMTQzLjg0NCwxNC4xOTEgMTQ0Ljg2LDEyLjc2MyAxNDYuNzA1LDExLjY5OCBMMTU2LjE5OCw2LjA5MSBDMTU2LjMwOSw2LjAyNSAxNTYuNDUyLDYuMDYyIDE1Ni41MTgsNi4xNzMgQzE1Ni41ODMsNi4yODQgMTU2LjU0Nyw2LjQyNyAxNTYuNDM2LDYuNDkzIEwxNDYuOTQsMTIuMTAyIEMxNDUuMjQ0LDEzLjA4MSAxNDQuMzEyLDE0LjM2NSAxNDQuMzEyLDE1LjcxOCBDMTQ0LjMxMiwxNy4wNTggMTQ1LjIzLDE4LjMyNiAxNDYuODk3LDE5LjI4OSBDMTUwLjQ0NiwyMS4zMzggMTU2LjI0LDIxLjMyNyAxNTkuODExLDE5LjI2NSBMMTY5LjU1OSwxMy42MzcgQzE2OS42NywxMy41NzMgMTY5LjgxMywxMy42MTEgMTY5Ljg3OCwxMy43MjMgQzE2OS45NDMsMTMuODM0IDE2OS45MDQsMTMuOTc3IDE2OS43OTMsMTQuMDQyIEwxNjAuMDQ1LDE5LjY3IEMxNTguMTg3LDIwLjc0MiAxNTUuNzQ5LDIxLjI3OSAxNTMuMzE2LDIxLjI3OSIgaWQ9IkZpbGwtMjUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3NS45OTIgTDY3Ljc2Miw0OS40ODQiIGlkPSJGaWxsLTI2IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMy42NzUsNzYuMzQyIEMxMTMuNjE1LDc2LjM0MiAxMTMuNTU1LDc2LjMyNyAxMTMuNSw3Ni4yOTUgTDY3LjU4Nyw0OS43ODcgQzY3LjQxOSw0OS42OSA2Ny4zNjIsNDkuNDc2IDY3LjQ1OSw0OS4zMDkgQzY3LjU1Niw0OS4xNDEgNjcuNzcsNDkuMDgzIDY3LjkzNyw0OS4xOCBMMTEzLjg1LDc1LjY4OCBDMTE0LjAxOCw3NS43ODUgMTE0LjA3NSw3NiAxMTMuOTc4LDc2LjE2NyBDMTEzLjkxNCw3Ni4yNzkgMTEzLjc5Niw3Ni4zNDIgMTEzLjY3NSw3Ni4zNDIiIGlkPSJGaWxsLTI3IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY3Ljc2Miw0OS40ODQgTDY3Ljc2MiwxMDMuNDg1IEM2Ny43NjIsMTA0LjU3NSA2OC41MzIsMTA1LjkwMyA2OS40ODIsMTA2LjQ1MiBMMTExLjk1NSwxMzAuOTczIEMxMTIuOTA1LDEzMS41MjIgMTEzLjY3NSwxMzEuMDgzIDExMy42NzUsMTI5Ljk5MyBMMTEzLjY3NSw3NS45OTIiIGlkPSJGaWxsLTI4IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMi43MjcsMTMxLjU2MSBDMTEyLjQzLDEzMS41NjEgMTEyLjEwNywxMzEuNDY2IDExMS43OCwxMzEuMjc2IEw2OS4zMDcsMTA2Ljc1NSBDNjguMjQ0LDEwNi4xNDIgNjcuNDEyLDEwNC43MDUgNjcuNDEyLDEwMy40ODUgTDY3LjQxMiw0OS40ODQgQzY3LjQxMiw0OS4yOSA2Ny41NjksNDkuMTM0IDY3Ljc2Miw0OS4xMzQgQzY3Ljk1Niw0OS4xMzQgNjguMTEzLDQ5LjI5IDY4LjExMyw0OS40ODQgTDY4LjExMywxMDMuNDg1IEM2OC4xMTMsMTA0LjQ0NSA2OC44MiwxMDUuNjY1IDY5LjY1NywxMDYuMTQ4IEwxMTIuMTMsMTMwLjY3IEMxMTIuNDc0LDEzMC44NjggMTEyLjc5MSwxMzAuOTEzIDExMywxMzAuNzkyIEMxMTMuMjA2LDEzMC42NzMgMTEzLjMyNSwxMzAuMzgxIDExMy4zMjUsMTI5Ljk5MyBMMTEzLjMyNSw3NS45OTIgQzExMy4zMjUsNzUuNzk4IDExMy40ODIsNzUuNjQxIDExMy42NzUsNzUuNjQxIEMxMTMuODY5LDc1LjY0MSAxMTQuMDI1LDc1Ljc5OCAxMTQuMDI1LDc1Ljk5MiBMMTE0LjAyNSwxMjkuOTkzIEMxMTQuMDI1LDEzMC42NDggMTEzLjc4NiwxMzEuMTQ3IDExMy4zNSwxMzEuMzk5IEMxMTMuMTYyLDEzMS41MDcgMTEyLjk1MiwxMzEuNTYxIDExMi43MjcsMTMxLjU2MSIgaWQ9IkZpbGwtMjkiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEyLjg2LDQwLjUxMiBDMTEyLjg2LDQwLjUxMiAxMTIuODYsNDAuNTEyIDExMi44NTksNDAuNTEyIEMxMTAuNTQxLDQwLjUxMiAxMDguMzYsMzkuOTkgMTA2LjcxNywzOS4wNDEgQzEwNS4wMTIsMzguMDU3IDEwNC4wNzQsMzYuNzI2IDEwNC4wNzQsMzUuMjkyIEMxMDQuMDc0LDMzLjg0NyAxMDUuMDI2LDMyLjUwMSAxMDYuNzU0LDMxLjUwNCBMMTE4Ljc5NSwyNC41NTEgQzEyMC40NjMsMjMuNTg5IDEyMi42NjksMjMuMDU4IDEyNS4wMDcsMjMuMDU4IEMxMjcuMzI1LDIzLjA1OCAxMjkuNTA2LDIzLjU4MSAxMzEuMTUsMjQuNTMgQzEzMi44NTQsMjUuNTE0IDEzMy43OTMsMjYuODQ1IDEzMy43OTMsMjguMjc4IEMxMzMuNzkzLDI5LjcyNCAxMzIuODQxLDMxLjA2OSAxMzEuMTEzLDMyLjA2NyBMMTE5LjA3MSwzOS4wMTkgQzExNy40MDMsMzkuOTgyIDExNS4xOTcsNDAuNTEyIDExMi44Niw0MC41MTIgTDExMi44Niw0MC41MTIgWiBNMTI1LjAwNywyMy43NTkgQzEyMi43OSwyMy43NTkgMTIwLjcwOSwyNC4yNTYgMTE5LjE0NiwyNS4xNTggTDEwNy4xMDQsMzIuMTEgQzEwNS42MDIsMzIuOTc4IDEwNC43NzQsMzQuMTA4IDEwNC43NzQsMzUuMjkyIEMxMDQuNzc0LDM2LjQ2NSAxMDUuNTg5LDM3LjU4MSAxMDcuMDY3LDM4LjQzNCBDMTA4LjYwNSwzOS4zMjMgMTEwLjY2MywzOS44MTIgMTEyLjg1OSwzOS44MTIgTDExMi44NiwzOS44MTIgQzExNS4wNzYsMzkuODEyIDExNy4xNTgsMzkuMzE1IDExOC43MjEsMzguNDEzIEwxMzAuNzYyLDMxLjQ2IEMxMzIuMjY0LDMwLjU5MyAxMzMuMDkyLDI5LjQ2MyAxMzMuMDkyLDI4LjI3OCBDMTMzLjA5MiwyNy4xMDYgMTMyLjI3OCwyNS45OSAxMzAuOCwyNS4xMzYgQzEyOS4yNjEsMjQuMjQ4IDEyNy4yMDQsMjMuNzU5IDEyNS4wMDcsMjMuNzU5IEwxMjUuMDA3LDIzLjc1OSBaIiBpZD0iRmlsbC0zMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNjUuNjMsMTYuMjE5IEwxNTkuODk2LDE5LjUzIEMxNTYuNzI5LDIxLjM1OCAxNTEuNjEsMjEuMzY3IDE0OC40NjMsMTkuNTUgQzE0NS4zMTYsMTcuNzMzIDE0NS4zMzIsMTQuNzc4IDE0OC40OTksMTIuOTQ5IEwxNTQuMjMzLDkuNjM5IEwxNjUuNjMsMTYuMjE5IiBpZD0iRmlsbC0zMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNTQuMjMzLDEwLjQ0OCBMMTY0LjIyOCwxNi4yMTkgTDE1OS41NDYsMTguOTIzIEMxNTguMTEyLDE5Ljc1IDE1Ni4xOTQsMjAuMjA2IDE1NC4xNDcsMjAuMjA2IEMxNTIuMTE4LDIwLjIwNiAxNTAuMjI0LDE5Ljc1NyAxNDguODE0LDE4Ljk0MyBDMTQ3LjUyNCwxOC4xOTkgMTQ2LjgxNCwxNy4yNDkgMTQ2LjgxNCwxNi4yNjkgQzE0Ni44MTQsMTUuMjc4IDE0Ny41MzcsMTQuMzE0IDE0OC44NSwxMy41NTYgTDE1NC4yMzMsMTAuNDQ4IE0xNTQuMjMzLDkuNjM5IEwxNDguNDk5LDEyLjk0OSBDMTQ1LjMzMiwxNC43NzggMTQ1LjMxNiwxNy43MzMgMTQ4LjQ2MywxOS41NSBDMTUwLjAzMSwyMC40NTUgMTUyLjA4NiwyMC45MDcgMTU0LjE0NywyMC45MDcgQzE1Ni4yMjQsMjAuOTA3IDE1OC4zMDYsMjAuNDQ3IDE1OS44OTYsMTkuNTMgTDE2NS42MywxNi4yMTkgTDE1NC4yMzMsOS42MzkiIGlkPSJGaWxsLTMyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NS40NDUsNzIuNjY3IEwxNDUuNDQ1LDcyLjY2NyBDMTQzLjY3Miw3Mi42NjcgMTQyLjIwNCw3MS44MTcgMTQxLjIwMiw3MC40MjIgQzE0MS4xMzUsNzAuMzMgMTQxLjE0NSw3MC4xNDcgMTQxLjIyNSw3MC4wNjYgQzE0MS4zMDUsNjkuOTg1IDE0MS40MzIsNjkuOTQ2IDE0MS41MjUsNzAuMDExIEMxNDIuMzA2LDcwLjU1OSAxNDMuMjMxLDcwLjgyMyAxNDQuMjc2LDcwLjgyMiBDMTQ1LjU5OCw3MC44MjIgMTQ3LjAzLDcwLjM3NiAxNDguNTMyLDY5LjUwOSBDMTUzLjg0Miw2Ni40NDMgMTU4LjE2Myw1OC45ODcgMTU4LjE2Myw1Mi44OTQgQzE1OC4xNjMsNTAuOTY3IDE1Ny43MjEsNDkuMzMyIDE1Ni44ODQsNDguMTY4IEMxNTYuODE4LDQ4LjA3NiAxNTYuODI4LDQ3Ljk0OCAxNTYuOTA4LDQ3Ljg2NyBDMTU2Ljk4OCw0Ny43ODYgMTU3LjExNCw0Ny43NzQgMTU3LjIwOCw0Ny44NCBDMTU4Ljg3OCw0OS4wMTIgMTU5Ljc5OCw1MS4yMiAxNTkuNzk4LDU0LjA1OSBDMTU5Ljc5OCw2MC4zMDEgMTU1LjM3Myw2OC4wNDYgMTQ5LjkzMyw3MS4xODYgQzE0OC4zNiw3Mi4wOTQgMTQ2Ljg1LDcyLjY2NyAxNDUuNDQ1LDcyLjY2NyBMMTQ1LjQ0NSw3Mi42NjcgWiBNMTQyLjQ3Niw3MSBDMTQzLjI5LDcxLjY1MSAxNDQuMjk2LDcyLjAwMiAxNDUuNDQ1LDcyLjAwMiBDMTQ2Ljc2Nyw3Mi4wMDIgMTQ4LjE5OCw3MS41NSAxNDkuNyw3MC42ODIgQzE1NS4wMSw2Ny42MTcgMTU5LjMzMSw2MC4xNTkgMTU5LjMzMSw1NC4wNjUgQzE1OS4zMzEsNTIuMDg1IDE1OC44NjgsNTAuNDM1IDE1OC4wMDYsNDkuMjcyIEMxNTguNDE3LDUwLjMwNyAxNTguNjMsNTEuNTMyIDE1OC42Myw1Mi44OTIgQzE1OC42Myw1OS4xMzQgMTU0LjIwNSw2Ni43NjcgMTQ4Ljc2NSw2OS45MDcgQzE0Ny4xOTIsNzAuODE2IDE0NS42ODEsNzEuMjgzIDE0NC4yNzYsNzEuMjgzIEMxNDMuNjM0LDcxLjI4MyAxNDMuMDMzLDcxLjE5MiAxNDIuNDc2LDcxIEwxNDIuNDc2LDcxIFoiIGlkPSJGaWxsLTMzIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0OC42NDgsNjkuNzA0IEMxNTQuMDMyLDY2LjU5NiAxNTguMzk2LDU5LjA2OCAxNTguMzk2LDUyLjg5MSBDMTU4LjM5Niw1MC44MzkgMTU3LjkxMyw0OS4xOTggMTU3LjA3NCw0OC4wMyBDMTU1LjI4OSw0Ni43NzggMTUyLjY5OSw0Ni44MzYgMTQ5LjgxNiw0OC41MDEgQzE0NC40MzMsNTEuNjA5IDE0MC4wNjgsNTkuMTM3IDE0MC4wNjgsNjUuMzE0IEMxNDAuMDY4LDY3LjM2NSAxNDAuNTUyLDY5LjAwNiAxNDEuMzkxLDcwLjE3NCBDMTQzLjE3Niw3MS40MjcgMTQ1Ljc2NSw3MS4zNjkgMTQ4LjY0OCw2OS43MDQiIGlkPSJGaWxsLTM0IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NC4yNzYsNzEuMjc2IEwxNDQuMjc2LDcxLjI3NiBDMTQzLjEzMyw3MS4yNzYgMTQyLjExOCw3MC45NjkgMTQxLjI1Nyw3MC4zNjUgQzE0MS4yMzYsNzAuMzUxIDE0MS4yMTcsNzAuMzMyIDE0MS4yMDIsNzAuMzExIEMxNDAuMzA3LDY5LjA2NyAxMzkuODM1LDY3LjMzOSAxMzkuODM1LDY1LjMxNCBDMTM5LjgzNSw1OS4wNzMgMTQ0LjI2LDUxLjQzOSAxNDkuNyw0OC4yOTggQzE1MS4yNzMsNDcuMzkgMTUyLjc4NCw0Ni45MjkgMTU0LjE4OSw0Ni45MjkgQzE1NS4zMzIsNDYuOTI5IDE1Ni4zNDcsNDcuMjM2IDE1Ny4yMDgsNDcuODM5IEMxNTcuMjI5LDQ3Ljg1NCAxNTcuMjQ4LDQ3Ljg3MyAxNTcuMjYzLDQ3Ljg5NCBDMTU4LjE1Nyw0OS4xMzggMTU4LjYzLDUwLjg2NSAxNTguNjMsNTIuODkxIEMxNTguNjMsNTkuMTMyIDE1NC4yMDUsNjYuNzY2IDE0OC43NjUsNjkuOTA3IEMxNDcuMTkyLDcwLjgxNSAxNDUuNjgxLDcxLjI3NiAxNDQuMjc2LDcxLjI3NiBMMTQ0LjI3Niw3MS4yNzYgWiBNMTQxLjU1OCw3MC4xMDQgQzE0Mi4zMzEsNzAuNjM3IDE0My4yNDUsNzEuMDA1IDE0NC4yNzYsNzEuMDA1IEMxNDUuNTk4LDcxLjAwNSAxNDcuMDMsNzAuNDY3IDE0OC41MzIsNjkuNiBDMTUzLjg0Miw2Ni41MzQgMTU4LjE2Myw1OS4wMzMgMTU4LjE2Myw1Mi45MzkgQzE1OC4xNjMsNTEuMDMxIDE1Ny43MjksNDkuMzg1IDE1Ni45MDcsNDguMjIzIEMxNTYuMTMzLDQ3LjY5MSAxNTUuMjE5LDQ3LjQwOSAxNTQuMTg5LDQ3LjQwOSBDMTUyLjg2Nyw0Ny40MDkgMTUxLjQzNSw0Ny44NDIgMTQ5LjkzMyw0OC43MDkgQzE0NC42MjMsNTEuNzc1IDE0MC4zMDIsNTkuMjczIDE0MC4zMDIsNjUuMzY2IEMxNDAuMzAyLDY3LjI3NiAxNDAuNzM2LDY4Ljk0MiAxNDEuNTU4LDcwLjEwNCBMMTQxLjU1OCw3MC4xMDQgWiIgaWQ9IkZpbGwtMzUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUwLjcyLDY1LjM2MSBMMTUwLjM1Nyw2NS4wNjYgQzE1MS4xNDcsNjQuMDkyIDE1MS44NjksNjMuMDQgMTUyLjUwNSw2MS45MzggQzE1My4zMTMsNjAuNTM5IDE1My45NzgsNTkuMDY3IDE1NC40ODIsNTcuNTYzIEwxNTQuOTI1LDU3LjcxMiBDMTU0LjQxMiw1OS4yNDUgMTUzLjczMyw2MC43NDUgMTUyLjkxLDYyLjE3MiBDMTUyLjI2Miw2My4yOTUgMTUxLjUyNSw2NC4zNjggMTUwLjcyLDY1LjM2MSIgaWQ9IkZpbGwtMzYiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE1LjkxNyw4NC41MTQgTDExNS41NTQsODQuMjIgQzExNi4zNDQsODMuMjQ1IDExNy4wNjYsODIuMTk0IDExNy43MDIsODEuMDkyIEMxMTguNTEsNzkuNjkyIDExOS4xNzUsNzguMjIgMTE5LjY3OCw3Ni43MTcgTDEyMC4xMjEsNzYuODY1IEMxMTkuNjA4LDc4LjM5OCAxMTguOTMsNzkuODk5IDExOC4xMDYsODEuMzI2IEMxMTcuNDU4LDgyLjQ0OCAxMTYuNzIyLDgzLjUyMSAxMTUuOTE3LDg0LjUxNCIgaWQ9IkZpbGwtMzciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE0LDEzMC40NzYgTDExNCwxMzAuMDA4IEwxMTQsNzYuMDUyIEwxMTQsNzUuNTg0IEwxMTQsNzYuMDUyIEwxMTQsMTMwLjAwOCBMMTE0LDEzMC40NzYiIGlkPSJGaWxsLTM4IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYyLjAwMDAwMCwgMC4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTkuODIyLDM3LjQ3NCBDMTkuODM5LDM3LjMzOSAxOS43NDcsMzcuMTk0IDE5LjU1NSwzNy4wODIgQzE5LjIyOCwzNi44OTQgMTguNzI5LDM2Ljg3MiAxOC40NDYsMzcuMDM3IEwxMi40MzQsNDAuNTA4IEMxMi4zMDMsNDAuNTg0IDEyLjI0LDQwLjY4NiAxMi4yNDMsNDAuNzkzIEMxMi4yNDUsNDAuOTI1IDEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQxLjM3MSBMMTIuMjQ1LDQxLjQxNCBMMTIuMjM4LDQxLjU0MiBDOC4xNDgsNDMuODg3IDUuNjQ3LDQ1LjMyMSA1LjY0Nyw0NS4zMjEgQzUuNjQ2LDQ1LjMyMSAzLjU3LDQ2LjM2NyAyLjg2LDUwLjUxMyBDMi44Niw1MC41MTMgMS45NDgsNTcuNDc0IDEuOTYyLDcwLjI1OCBDMS45NzcsODIuODI4IDIuNTY4LDg3LjMyOCAzLjEyOSw5MS42MDkgQzMuMzQ5LDkzLjI5MyA2LjEzLDkzLjczNCA2LjEzLDkzLjczNCBDNi40NjEsOTMuNzc0IDYuODI4LDkzLjcwNyA3LjIxLDkzLjQ4NiBMODIuNDgzLDQ5LjkzNSBDODQuMjkxLDQ4Ljg2NiA4NS4xNSw0Ni4yMTYgODUuNTM5LDQzLjY1MSBDODYuNzUyLDM1LjY2MSA4Ny4yMTQsMTAuNjczIDg1LjI2NCwzLjc3MyBDODUuMDY4LDMuMDggODQuNzU0LDIuNjkgODQuMzk2LDIuNDkxIEw4Mi4zMSwxLjcwMSBDODEuNTgzLDEuNzI5IDgwLjg5NCwyLjE2OCA4MC43NzYsMi4yMzYgQzgwLjYzNiwyLjMxNyA0MS44MDcsMjQuNTg1IDIwLjAzMiwzNy4wNzIgTDE5LjgyMiwzNy40NzQiIGlkPSJGaWxsLTEiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNODIuMzExLDEuNzAxIEw4NC4zOTYsMi40OTEgQzg0Ljc1NCwyLjY5IDg1LjA2OCwzLjA4IDg1LjI2NCwzLjc3MyBDODcuMjEzLDEwLjY3MyA4Ni43NTEsMzUuNjYgODUuNTM5LDQzLjY1MSBDODUuMTQ5LDQ2LjIxNiA4NC4yOSw0OC44NjYgODIuNDgzLDQ5LjkzNSBMNy4yMSw5My40ODYgQzYuODk3LDkzLjY2NyA2LjU5NSw5My43NDQgNi4zMTQsOTMuNzQ0IEw2LjEzMSw5My43MzMgQzYuMTMxLDkzLjczNCAzLjM0OSw5My4yOTMgMy4xMjgsOTEuNjA5IEMyLjU2OCw4Ny4zMjcgMS45NzcsODIuODI4IDEuOTYzLDcwLjI1OCBDMS45NDgsNTcuNDc0IDIuODYsNTAuNTEzIDIuODYsNTAuNTEzIEMzLjU3LDQ2LjM2NyA1LjY0Nyw0NS4zMjEgNS42NDcsNDUuMzIxIEM1LjY0Nyw0NS4zMjEgOC4xNDgsNDMuODg3IDEyLjIzOCw0MS41NDIgTDEyLjI0NSw0MS40MTQgTDEyLjI0NSw0MS4zNzEgQzEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQwLjkyNSAxMi4yNDMsNDAuNzkzIEMxMi4yNCw0MC42ODYgMTIuMzAyLDQwLjU4MyAxMi40MzQsNDAuNTA4IEwxOC40NDYsMzcuMDM2IEMxOC41NzQsMzYuOTYyIDE4Ljc0NiwzNi45MjYgMTguOTI3LDM2LjkyNiBDMTkuMTQ1LDM2LjkyNiAxOS4zNzYsMzYuOTc5IDE5LjU1NCwzNy4wODIgQzE5Ljc0NywzNy4xOTQgMTkuODM5LDM3LjM0IDE5LjgyMiwzNy40NzQgTDIwLjAzMywzNy4wNzIgQzQxLjgwNiwyNC41ODUgODAuNjM2LDIuMzE4IDgwLjc3NywyLjIzNiBDODAuODk0LDIuMTY4IDgxLjU4MywxLjcyOSA4Mi4zMTEsMS43MDEgTTgyLjMxMSwwLjcwNCBMODIuMjcyLDAuNzA1IEM4MS42NTQsMC43MjggODAuOTg5LDAuOTQ5IDgwLjI5OCwxLjM2MSBMODAuMjc3LDEuMzczIEM4MC4xMjksMS40NTggNTkuNzY4LDEzLjEzNSAxOS43NTgsMzYuMDc5IEMxOS41LDM1Ljk4MSAxOS4yMTQsMzUuOTI5IDE4LjkyNywzNS45MjkgQzE4LjU2MiwzNS45MjkgMTguMjIzLDM2LjAxMyAxNy45NDcsMzYuMTczIEwxMS45MzUsMzkuNjQ0IEMxMS40OTMsMzkuODk5IDExLjIzNiw0MC4zMzQgMTEuMjQ2LDQwLjgxIEwxMS4yNDcsNDAuOTYgTDUuMTY3LDQ0LjQ0NyBDNC43OTQsNDQuNjQ2IDIuNjI1LDQ1Ljk3OCAxLjg3Nyw1MC4zNDUgTDEuODcxLDUwLjM4NCBDMS44NjIsNTAuNDU0IDAuOTUxLDU3LjU1NyAwLjk2NSw3MC4yNTkgQzAuOTc5LDgyLjg3OSAxLjU2OCw4Ny4zNzUgMi4xMzcsOTEuNzI0IEwyLjEzOSw5MS43MzkgQzIuNDQ3LDk0LjA5NCA1LjYxNCw5NC42NjIgNS45NzUsOTQuNzE5IEw2LjAwOSw5NC43MjMgQzYuMTEsOTQuNzM2IDYuMjEzLDk0Ljc0MiA2LjMxNCw5NC43NDIgQzYuNzksOTQuNzQyIDcuMjYsOTQuNjEgNy43MSw5NC4zNSBMODIuOTgzLDUwLjc5OCBDODQuNzk0LDQ5LjcyNyA4NS45ODIsNDcuMzc1IDg2LjUyNSw0My44MDEgQzg3LjcxMSwzNS45ODcgODguMjU5LDEwLjcwNSA4Ni4yMjQsMy41MDIgQzg1Ljk3MSwyLjYwOSA4NS41MiwxLjk3NSA4NC44ODEsMS42MiBMODQuNzQ5LDEuNTU4IEw4Mi42NjQsMC43NjkgQzgyLjU1MSwwLjcyNSA4Mi40MzEsMC43MDQgODIuMzExLDAuNzA0IiBpZD0iRmlsbC0yIiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY2LjI2NywxMS41NjUgTDY3Ljc2MiwxMS45OTkgTDExLjQyMyw0NC4zMjUiIGlkPSJGaWxsLTMiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMjAyLDkwLjU0NSBDMTIuMDI5LDkwLjU0NSAxMS44NjIsOTAuNDU1IDExLjc2OSw5MC4yOTUgQzExLjYzMiw5MC4wNTcgMTEuNzEzLDg5Ljc1MiAxMS45NTIsODkuNjE0IEwzMC4zODksNzguOTY5IEMzMC42MjgsNzguODMxIDMwLjkzMyw3OC45MTMgMzEuMDcxLDc5LjE1MiBDMzEuMjA4LDc5LjM5IDMxLjEyNyw3OS42OTYgMzAuODg4LDc5LjgzMyBMMTIuNDUxLDkwLjQ3OCBMMTIuMjAyLDkwLjU0NSIgaWQ9IkZpbGwtNCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMy43NjQsNDIuNjU0IEwxMy42NTYsNDIuNTkyIEwxMy43MDIsNDIuNDIxIEwxOC44MzcsMzkuNDU3IEwxOS4wMDcsMzkuNTAyIEwxOC45NjIsMzkuNjczIEwxMy44MjcsNDIuNjM3IEwxMy43NjQsNDIuNjU0IiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTguNTIsOTAuMzc1IEw4LjUyLDQ2LjQyMSBMOC41ODMsNDYuMzg1IEw3NS44NCw3LjU1NCBMNzUuODQsNTEuNTA4IEw3NS43NzgsNTEuNTQ0IEw4LjUyLDkwLjM3NSBMOC41Miw5MC4zNzUgWiBNOC43Nyw0Ni41NjQgTDguNzcsODkuOTQ0IEw3NS41OTEsNTEuMzY1IEw3NS41OTEsNy45ODUgTDguNzcsNDYuNTY0IEw4Ljc3LDQ2LjU2NCBaIiBpZD0iRmlsbC02IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTI0Ljk4Niw4My4xODIgQzI0Ljc1Niw4My4zMzEgMjQuMzc0LDgzLjU2NiAyNC4xMzcsODMuNzA1IEwxMi42MzIsOTAuNDA2IEMxMi4zOTUsOTAuNTQ1IDEyLjQyNiw5MC42NTggMTIuNyw5MC42NTggTDEzLjI2NSw5MC42NTggQzEzLjU0LDkwLjY1OCAxMy45NTgsOTAuNTQ1IDE0LjE5NSw5MC40MDYgTDI1LjcsODMuNzA1IEMyNS45MzcsODMuNTY2IDI2LjEyOCw4My40NTIgMjYuMTI1LDgzLjQ0OSBDMjYuMTIyLDgzLjQ0NyAyNi4xMTksODMuMjIgMjYuMTE5LDgyLjk0NiBDMjYuMTE5LDgyLjY3MiAyNS45MzEsODIuNTY5IDI1LjcwMSw4Mi43MTkgTDI0Ljk4Niw4My4xODIiIGlkPSJGaWxsLTciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTMuMjY2LDkwLjc4MiBMMTIuNyw5MC43ODIgQzEyLjUsOTAuNzgyIDEyLjM4NCw5MC43MjYgMTIuMzU0LDkwLjYxNiBDMTIuMzI0LDkwLjUwNiAxMi4zOTcsOTAuMzk5IDEyLjU2OSw5MC4yOTkgTDI0LjA3NCw4My41OTcgQzI0LjMxLDgzLjQ1OSAyNC42ODksODMuMjI2IDI0LjkxOCw4My4wNzggTDI1LjYzMyw4Mi42MTQgQzI1LjcyMyw4Mi41NTUgMjUuODEzLDgyLjUyNSAyNS44OTksODIuNTI1IEMyNi4wNzEsODIuNTI1IDI2LjI0NCw4Mi42NTUgMjYuMjQ0LDgyLjk0NiBDMjYuMjQ0LDgzLjE2IDI2LjI0NSw4My4zMDkgMjYuMjQ3LDgzLjM4MyBMMjYuMjUzLDgzLjM4NyBMMjYuMjQ5LDgzLjQ1NiBDMjYuMjQ2LDgzLjUzMSAyNi4yNDYsODMuNTMxIDI1Ljc2Myw4My44MTIgTDE0LjI1OCw5MC41MTQgQzE0LDkwLjY2NSAxMy41NjQsOTAuNzgyIDEzLjI2Niw5MC43ODIgTDEzLjI2Niw5MC43ODIgWiBNMTIuNjY2LDkwLjUzMiBMMTIuNyw5MC41MzMgTDEzLjI2Niw5MC41MzMgQzEzLjUxOCw5MC41MzMgMTMuOTE1LDkwLjQyNSAxNC4xMzIsOTAuMjk5IEwyNS42MzcsODMuNTk3IEMyNS44MDUsODMuNDk5IDI1LjkzMSw4My40MjQgMjUuOTk4LDgzLjM4MyBDMjUuOTk0LDgzLjI5OSAyNS45OTQsODMuMTY1IDI1Ljk5NCw4Mi45NDYgTDI1Ljg5OSw4Mi43NzUgTDI1Ljc2OCw4Mi44MjQgTDI1LjA1NCw4My4yODcgQzI0LjgyMiw4My40MzcgMjQuNDM4LDgzLjY3MyAyNC4yLDgzLjgxMiBMMTIuNjk1LDkwLjUxNCBMMTIuNjY2LDkwLjUzMiBMMTIuNjY2LDkwLjUzMiBaIiBpZD0iRmlsbC04IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEzLjI2Niw4OS44NzEgTDEyLjcsODkuODcxIEMxMi41LDg5Ljg3MSAxMi4zODQsODkuODE1IDEyLjM1NCw4OS43MDUgQzEyLjMyNCw4OS41OTUgMTIuMzk3LDg5LjQ4OCAxMi41NjksODkuMzg4IEwyNC4wNzQsODIuNjg2IEMyNC4zMzIsODIuNTM1IDI0Ljc2OCw4Mi40MTggMjUuMDY3LDgyLjQxOCBMMjUuNjMyLDgyLjQxOCBDMjUuODMyLDgyLjQxOCAyNS45NDgsODIuNDc0IDI1Ljk3OCw4Mi41ODQgQzI2LjAwOCw4Mi42OTQgMjUuOTM1LDgyLjgwMSAyNS43NjMsODIuOTAxIEwxNC4yNTgsODkuNjAzIEMxNCw4OS43NTQgMTMuNTY0LDg5Ljg3MSAxMy4yNjYsODkuODcxIEwxMy4yNjYsODkuODcxIFogTTEyLjY2Niw4OS42MjEgTDEyLjcsODkuNjIyIEwxMy4yNjYsODkuNjIyIEMxMy41MTgsODkuNjIyIDEzLjkxNSw4OS41MTUgMTQuMTMyLDg5LjM4OCBMMjUuNjM3LDgyLjY4NiBMMjUuNjY3LDgyLjY2OCBMMjUuNjMyLDgyLjY2NyBMMjUuMDY3LDgyLjY2NyBDMjQuODE1LDgyLjY2NyAyNC40MTgsODIuNzc1IDI0LjIsODIuOTAxIEwxMi42OTUsODkuNjAzIEwxMi42NjYsODkuNjIxIEwxMi42NjYsODkuNjIxIFoiIGlkPSJGaWxsLTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMzcsOTAuODAxIEwxMi4zNyw4OS41NTQgTDEyLjM3LDkwLjgwMSIgaWQ9IkZpbGwtMTAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNi4xMyw5My45MDEgQzUuMzc5LDkzLjgwOCA0LjgxNiw5My4xNjQgNC42OTEsOTIuNTI1IEMzLjg2LDg4LjI4NyAzLjU0LDgzLjc0MyAzLjUyNiw3MS4xNzMgQzMuNTExLDU4LjM4OSA0LjQyMyw1MS40MjggNC40MjMsNTEuNDI4IEM1LjEzNCw0Ny4yODIgNy4yMSw0Ni4yMzYgNy4yMSw0Ni4yMzYgQzcuMjEsNDYuMjM2IDgxLjY2NywzLjI1IDgyLjA2OSwzLjAxNyBDODIuMjkyLDIuODg4IDg0LjU1NiwxLjQzMyA4NS4yNjQsMy45NCBDODcuMjE0LDEwLjg0IDg2Ljc1MiwzNS44MjcgODUuNTM5LDQzLjgxOCBDODUuMTUsNDYuMzgzIDg0LjI5MSw0OS4wMzMgODIuNDgzLDUwLjEwMSBMNy4yMSw5My42NTMgQzYuODI4LDkzLjg3NCA2LjQ2MSw5My45NDEgNi4xMyw5My45MDEgQzYuMTMsOTMuOTAxIDMuMzQ5LDkzLjQ2IDMuMTI5LDkxLjc3NiBDMi41NjgsODcuNDk1IDEuOTc3LDgyLjk5NSAxLjk2Miw3MC40MjUgQzEuOTQ4LDU3LjY0MSAyLjg2LDUwLjY4IDIuODYsNTAuNjggQzMuNTcsNDYuNTM0IDUuNjQ3LDQ1LjQ4OSA1LjY0Nyw0NS40ODkgQzUuNjQ2LDQ1LjQ4OSA4LjA2NSw0NC4wOTIgMTIuMjQ1LDQxLjY3OSBMMTMuMTE2LDQxLjU2IEwxOS43MTUsMzcuNzMgTDE5Ljc2MSwzNy4yNjkgTDYuMTMsOTMuOTAxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjMxNyw5NC4xNjEgTDYuMTAyLDk0LjE0OCBMNi4xMDEsOTQuMTQ4IEw1Ljg1Nyw5NC4xMDEgQzUuMTM4LDkzLjk0NSAzLjA4NSw5My4zNjUgMi44ODEsOTEuODA5IEMyLjMxMyw4Ny40NjkgMS43MjcsODIuOTk2IDEuNzEzLDcwLjQyNSBDMS42OTksNTcuNzcxIDIuNjA0LDUwLjcxOCAyLjYxMyw1MC42NDggQzMuMzM4LDQ2LjQxNyA1LjQ0NSw0NS4zMSA1LjUzNSw0NS4yNjYgTDEyLjE2Myw0MS40MzkgTDEzLjAzMyw0MS4zMiBMMTkuNDc5LDM3LjU3OCBMMTkuNTEzLDM3LjI0NCBDMTkuNTI2LDM3LjEwNyAxOS42NDcsMzcuMDA4IDE5Ljc4NiwzNy4wMjEgQzE5LjkyMiwzNy4wMzQgMjAuMDIzLDM3LjE1NiAyMC4wMDksMzcuMjkzIEwxOS45NSwzNy44ODIgTDEzLjE5OCw0MS44MDEgTDEyLjMyOCw0MS45MTkgTDUuNzcyLDQ1LjcwNCBDNS43NDEsNDUuNzIgMy43ODIsNDYuNzcyIDMuMTA2LDUwLjcyMiBDMy4wOTksNTAuNzgyIDIuMTk4LDU3LjgwOCAyLjIxMiw3MC40MjQgQzIuMjI2LDgyLjk2MyAyLjgwOSw4Ny40MiAzLjM3Myw5MS43MjkgQzMuNDY0LDkyLjQyIDQuMDYyLDkyLjg4MyA0LjY4Miw5My4xODEgQzQuNTY2LDkyLjk4NCA0LjQ4Niw5Mi43NzYgNC40NDYsOTIuNTcyIEMzLjY2NSw4OC41ODggMy4yOTEsODQuMzcgMy4yNzYsNzEuMTczIEMzLjI2Miw1OC41MiA0LjE2Nyw1MS40NjYgNC4xNzYsNTEuMzk2IEM0LjkwMSw0Ny4xNjUgNy4wMDgsNDYuMDU5IDcuMDk4LDQ2LjAxNCBDNy4wOTQsNDYuMDE1IDgxLjU0MiwzLjAzNCA4MS45NDQsMi44MDIgTDgxLjk3MiwyLjc4NSBDODIuODc2LDIuMjQ3IDgzLjY5MiwyLjA5NyA4NC4zMzIsMi4zNTIgQzg0Ljg4NywyLjU3MyA4NS4yODEsMy4wODUgODUuNTA0LDMuODcyIEM4Ny41MTgsMTEgODYuOTY0LDM2LjA5MSA4NS43ODUsNDMuODU1IEM4NS4yNzgsNDcuMTk2IDg0LjIxLDQ5LjM3IDgyLjYxLDUwLjMxNyBMNy4zMzUsOTMuODY5IEM2Ljk5OSw5NC4wNjMgNi42NTgsOTQuMTYxIDYuMzE3LDk0LjE2MSBMNi4zMTcsOTQuMTYxIFogTTYuMTcsOTMuNjU0IEM2LjQ2Myw5My42OSA2Ljc3NCw5My42MTcgNy4wODUsOTMuNDM3IEw4Mi4zNTgsNDkuODg2IEM4NC4xODEsNDguODA4IDg0Ljk2LDQ1Ljk3MSA4NS4yOTIsNDMuNzggQzg2LjQ2NiwzNi4wNDkgODcuMDIzLDExLjA4NSA4NS4wMjQsNC4wMDggQzg0Ljg0NiwzLjM3NyA4NC41NTEsMi45NzYgODQuMTQ4LDIuODE2IEM4My42NjQsMi42MjMgODIuOTgyLDIuNzY0IDgyLjIyNywzLjIxMyBMODIuMTkzLDMuMjM0IEM4MS43OTEsMy40NjYgNy4zMzUsNDYuNDUyIDcuMzM1LDQ2LjQ1MiBDNy4zMDQsNDYuNDY5IDUuMzQ2LDQ3LjUyMSA0LjY2OSw1MS40NzEgQzQuNjYyLDUxLjUzIDMuNzYxLDU4LjU1NiAzLjc3NSw3MS4xNzMgQzMuNzksODQuMzI4IDQuMTYxLDg4LjUyNCA0LjkzNiw5Mi40NzYgQzUuMDI2LDkyLjkzNyA1LjQxMiw5My40NTkgNS45NzMsOTMuNjE1IEM2LjA4Nyw5My42NCA2LjE1OCw5My42NTIgNi4xNjksOTMuNjU0IEw2LjE3LDkzLjY1NCBMNi4xNyw5My42NTQgWiIgaWQ9IkZpbGwtMTIiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4zMTcsNjguOTgyIEM3LjgwNiw2OC43MDEgOC4yMDIsNjguOTI2IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNi44MjksNzEuMjk0IDYuNDMzLDcxLjA2OSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIiBpZD0iRmlsbC0xMyIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjkyLDcxLjEzMyBDNi42MzEsNzEuMTMzIDYuNDMzLDcwLjkwNSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIEM3LjQ2LDY4LjkgNy41OTUsNjguODYxIDcuNzE0LDY4Ljg2MSBDOC4wMDMsNjguODYxIDguMjAyLDY5LjA5IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNy4xNzQsNzEuMDk0IDcuMDM5LDcxLjEzMyA2LjkyLDcxLjEzMyBNNy43MTQsNjguNjc0IEM3LjU1Nyw2OC42NzQgNy4zOTIsNjguNzIzIDcuMjI0LDY4LjgyMSBDNi42NzYsNjkuMTM4IDYuMjQ2LDY5Ljg3OSA2LjI0Niw3MC41MDggQzYuMjQ2LDcwLjk5NCA2LjUxNyw3MS4zMiA2LjkyLDcxLjMyIEM3LjA3OCw3MS4zMiA3LjI0Myw3MS4yNzEgNy40MTEsNzEuMTc0IEM3Ljk1OSw3MC44NTcgOC4zODksNzAuMTE3IDguMzg5LDY5LjQ4NyBDOC4zODksNjkuMDAxIDguMTE3LDY4LjY3NCA3LjcxNCw2OC42NzQiIGlkPSJGaWxsLTE0IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYuOTIsNzAuOTQ3IEM2LjY0OSw3MC45NDcgNi42MjEsNzAuNjQgNi42MjEsNzAuNTA4IEM2LjYyMSw3MC4wMTcgNi45ODIsNjkuMzkyIDcuNDExLDY5LjE0NSBDNy41MjEsNjkuMDgyIDcuNjI1LDY5LjA0OSA3LjcxNCw2OS4wNDkgQzcuOTg2LDY5LjA0OSA4LjAxNSw2OS4zNTUgOC4wMTUsNjkuNDg3IEM4LjAxNSw2OS45NzggNy42NTIsNzAuNjAzIDcuMjI0LDcwLjg1MSBDNy4xMTUsNzAuOTE0IDcuMDEsNzAuOTQ3IDYuOTIsNzAuOTQ3IE03LjcxNCw2OC44NjEgQzcuNTk1LDY4Ljg2MSA3LjQ2LDY4LjkgNy4zMTcsNjguOTgyIEM2LjgyOSw2OS4yNjUgNi40MzMsNjkuOTQ4IDYuNDMzLDcwLjUwOCBDNi40MzMsNzAuOTA1IDYuNjMxLDcxLjEzMyA2LjkyLDcxLjEzMyBDNy4wMzksNzEuMTMzIDcuMTc0LDcxLjA5NCA3LjMxNyw3MS4wMTIgQzcuODA2LDcwLjczIDguMjAyLDcwLjA0NyA4LjIwMiw2OS40ODcgQzguMjAyLDY5LjA5IDguMDAzLDY4Ljg2MSA3LjcxNCw2OC44NjEiIGlkPSJGaWxsLTE1IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTcuNDQ0LDg1LjM1IEM3LjcwOCw4NS4xOTggNy45MjEsODUuMzE5IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuOTI1IDcuNzA4LDg2LjI5MiA3LjQ0NCw4Ni40NDQgQzcuMTgxLDg2LjU5NyA2Ljk2Nyw4Ni40NzUgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IiBpZD0iRmlsbC0xNiIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik03LjIzLDg2LjUxIEM3LjA3NCw4Ni41MSA2Ljk2Nyw4Ni4zODcgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IEM3LjUyMSw4NS4zMDUgNy41OTQsODUuMjg0IDcuNjU4LDg1LjI4NCBDNy44MTQsODUuMjg0IDcuOTIxLDg1LjQwOCA3LjkyMSw4NS42MjIgQzcuOTIxLDg1LjkyNSA3LjcwOCw4Ni4yOTIgNy40NDQsODYuNDQ0IEM3LjM2Nyw4Ni40ODkgNy4yOTQsODYuNTEgNy4yMyw4Ni41MSBNNy42NTgsODUuMDk4IEM3LjU1OCw4NS4wOTggNy40NTUsODUuMTI3IDcuMzUxLDg1LjE4OCBDNy4wMzEsODUuMzczIDYuNzgxLDg1LjgwNiA2Ljc4MSw4Ni4xNzMgQzYuNzgxLDg2LjQ4MiA2Ljk2Niw4Ni42OTcgNy4yMyw4Ni42OTcgQzcuMzMsODYuNjk3IDcuNDMzLDg2LjY2NiA3LjUzOCw4Ni42MDcgQzcuODU4LDg2LjQyMiA4LjEwOCw4NS45ODkgOC4xMDgsODUuNjIyIEM4LjEwOCw4NS4zMTMgNy45MjMsODUuMDk4IDcuNjU4LDg1LjA5OCIgaWQ9IkZpbGwtMTciIGZpbGw9IiM4MDk3QTIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4yMyw4Ni4zMjIgTDcuMTU0LDg2LjE3MyBDNy4xNTQsODUuOTM4IDcuMzMzLDg1LjYyOSA3LjUzOCw4NS41MTIgTDcuNjU4LDg1LjQ3MSBMNy43MzQsODUuNjIyIEM3LjczNCw4NS44NTYgNy41NTUsODYuMTY0IDcuMzUxLDg2LjI4MiBMNy4yMyw4Ni4zMjIgTTcuNjU4LDg1LjI4NCBDNy41OTQsODUuMjg0IDcuNTIxLDg1LjMwNSA3LjQ0NCw4NS4zNSBDNy4xODEsODUuNTAyIDYuOTY3LDg1Ljg3MSA2Ljk2Nyw4Ni4xNzMgQzYuOTY3LDg2LjM4NyA3LjA3NCw4Ni41MSA3LjIzLDg2LjUxIEM3LjI5NCw4Ni41MSA3LjM2Nyw4Ni40ODkgNy40NDQsODYuNDQ0IEM3LjcwOCw4Ni4yOTIgNy45MjEsODUuOTI1IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuNDA4IDcuODE0LDg1LjI4NCA3LjY1OCw4NS4yODQiIGlkPSJGaWxsLTE4IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTc3LjI3OCw3Ljc2OSBMNzcuMjc4LDUxLjQzNiBMMTAuMjA4LDkwLjE2IEwxMC4yMDgsNDYuNDkzIEw3Ny4yNzgsNy43NjkiIGlkPSJGaWxsLTE5IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEwLjA4Myw5MC4zNzUgTDEwLjA4Myw0Ni40MjEgTDEwLjE0Niw0Ni4zODUgTDc3LjQwMyw3LjU1NCBMNzcuNDAzLDUxLjUwOCBMNzcuMzQxLDUxLjU0NCBMMTAuMDgzLDkwLjM3NSBMMTAuMDgzLDkwLjM3NSBaIE0xMC4zMzMsNDYuNTY0IEwxMC4zMzMsODkuOTQ0IEw3Ny4xNTQsNTEuMzY1IEw3Ny4xNTQsNy45ODUgTDEwLjMzMyw0Ni41NjQgTDEwLjMzMyw0Ni41NjQgWiIgaWQ9IkZpbGwtMjAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMjUuNzM3LDg4LjY0NyBMMTE4LjA5OCw5MS45ODEgTDExOC4wOTgsODQgTDEwNi42MzksODguNzEzIEwxMDYuNjM5LDk2Ljk4MiBMOTksMTAwLjMxNSBMMTEyLjM2OSwxMDMuOTYxIEwxMjUuNzM3LDg4LjY0NyIgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTIiIGZpbGw9IiM0NTVBNjQiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+';
+ function RotateInstructions() {
+ this.loadIcon_();
+ var overlay = document.createElement('div');
+ var s = overlay.style;
+ s.position = 'fixed';
+ s.top = 0;
+ s.right = 0;
+ s.bottom = 0;
+ s.left = 0;
+ s.backgroundColor = 'gray';
+ s.fontFamily = 'sans-serif';
+ s.zIndex = 1000000;
+ var img = document.createElement('img');
+ img.src = this.icon;
+ var s = img.style;
+ s.marginLeft = '25%';
+ s.marginTop = '25%';
+ s.width = '50%';
+ overlay.appendChild(img);
+ var text = document.createElement('div');
+ var s = text.style;
+ s.textAlign = 'center';
+ s.fontSize = '16px';
+ s.lineHeight = '24px';
+ s.margin = '24px 25%';
+ s.width = '50%';
+ text.innerHTML = 'Place your phone into your Cardboard viewer.';
+ overlay.appendChild(text);
+ var snackbar = document.createElement('div');
+ var s = snackbar.style;
+ s.backgroundColor = '#CFD8DC';
+ s.position = 'fixed';
+ s.bottom = 0;
+ s.width = '100%';
+ s.height = '48px';
+ s.padding = '14px 24px';
+ s.boxSizing = 'border-box';
+ s.color = '#656A6B';
+ overlay.appendChild(snackbar);
+ var snackbarText = document.createElement('div');
+ snackbarText.style.float = 'left';
+ snackbarText.innerHTML = 'No Cardboard viewer?';
+ var snackbarButton = document.createElement('a');
+ snackbarButton.href = 'https://www.google.com/get/cardboard/get-cardboard/';
+ snackbarButton.innerHTML = 'get one';
+ snackbarButton.target = '_blank';
+ var s = snackbarButton.style;
+ s.float = 'right';
+ s.fontWeight = 600;
+ s.textTransform = 'uppercase';
+ s.borderLeft = '1px solid gray';
+ s.paddingLeft = '24px';
+ s.textDecoration = 'none';
+ s.color = '#656A6B';
+ snackbar.appendChild(snackbarText);
+ snackbar.appendChild(snackbarButton);
+ this.overlay = overlay;
+ this.text = text;
+ this.hide();
+ }
+ RotateInstructions.prototype.show = function (parent) {
+ if (!parent && !this.overlay.parentElement) {
+ document.body.appendChild(this.overlay);
+ } else if (parent) {
+ if (this.overlay.parentElement && this.overlay.parentElement != parent) this.overlay.parentElement.removeChild(this.overlay);
+ parent.appendChild(this.overlay);
+ }
+ this.overlay.style.display = 'block';
+ var img = this.overlay.querySelector('img');
+ var s = img.style;
+ if (isLandscapeMode()) {
+ s.width = '20%';
+ s.marginLeft = '40%';
+ s.marginTop = '3%';
+ } else {
+ s.width = '50%';
+ s.marginLeft = '25%';
+ s.marginTop = '25%';
+ }
+ };
+ RotateInstructions.prototype.hide = function () {
+ this.overlay.style.display = 'none';
+ };
+ RotateInstructions.prototype.showTemporarily = function (ms, parent) {
+ this.show(parent);
+ this.timer = setTimeout(this.hide.bind(this), ms);
+ };
+ RotateInstructions.prototype.disableShowTemporarily = function () {
+ clearTimeout(this.timer);
+ };
+ RotateInstructions.prototype.update = function () {
+ this.disableShowTemporarily();
+ if (!isLandscapeMode() && isMobile()) {
+ this.show();
+ } else {
+ this.hide();
+ }
+ };
+ RotateInstructions.prototype.loadIcon_ = function () {
+ this.icon = base64('image/svg+xml', rotateInstructionsAsset);
+ };
+ var DEFAULT_VIEWER = 'CardboardV1';
+ var VIEWER_KEY = 'WEBVR_CARDBOARD_VIEWER';
+ var CLASS_NAME = 'webvr-polyfill-viewer-selector';
+ function ViewerSelector(defaultViewer) {
+ try {
+ this.selectedKey = localStorage.getItem(VIEWER_KEY);
+ } catch (error) {
+ console.error('Failed to load viewer profile: %s', error);
+ }
+ if (!this.selectedKey) {
+ this.selectedKey = defaultViewer || DEFAULT_VIEWER;
+ }
+ this.dialog = this.createDialog_(DeviceInfo.Viewers);
+ this.root = null;
+ this.onChangeCallbacks_ = [];
+ }
+ ViewerSelector.prototype.show = function (root) {
+ this.root = root;
+ root.appendChild(this.dialog);
+ var selected = this.dialog.querySelector('#' + this.selectedKey);
+ selected.checked = true;
+ this.dialog.style.display = 'block';
+ };
+ ViewerSelector.prototype.hide = function () {
+ if (this.root && this.root.contains(this.dialog)) {
+ this.root.removeChild(this.dialog);
+ }
+ this.dialog.style.display = 'none';
+ };
+ ViewerSelector.prototype.getCurrentViewer = function () {
+ return DeviceInfo.Viewers[this.selectedKey];
+ };
+ ViewerSelector.prototype.getSelectedKey_ = function () {
+ var input = this.dialog.querySelector('input[name=field]:checked');
+ if (input) {
+ return input.id;
+ }
+ return null;
+ };
+ ViewerSelector.prototype.onChange = function (cb) {
+ this.onChangeCallbacks_.push(cb);
+ };
+ ViewerSelector.prototype.fireOnChange_ = function (viewer) {
+ for (var i = 0; i < this.onChangeCallbacks_.length; i++) {
+ this.onChangeCallbacks_[i](viewer);
+ }
+ };
+ ViewerSelector.prototype.onSave_ = function () {
+ this.selectedKey = this.getSelectedKey_();
+ if (!this.selectedKey || !DeviceInfo.Viewers[this.selectedKey]) {
+ console.error('ViewerSelector.onSave_: this should never happen!');
+ return;
+ }
+ this.fireOnChange_(DeviceInfo.Viewers[this.selectedKey]);
+ try {
+ localStorage.setItem(VIEWER_KEY, this.selectedKey);
+ } catch (error) {
+ console.error('Failed to save viewer profile: %s', error);
+ }
+ this.hide();
+ };
+ ViewerSelector.prototype.createDialog_ = function (options) {
+ var container = document.createElement('div');
+ container.classList.add(CLASS_NAME);
+ container.style.display = 'none';
+ var overlay = document.createElement('div');
+ var s = overlay.style;
+ s.position = 'fixed';
+ s.left = 0;
+ s.top = 0;
+ s.width = '100%';
+ s.height = '100%';
+ s.background = 'rgba(0, 0, 0, 0.3)';
+ overlay.addEventListener('click', this.hide.bind(this));
+ var width = 280;
+ var dialog = document.createElement('div');
+ var s = dialog.style;
+ s.boxSizing = 'border-box';
+ s.position = 'fixed';
+ s.top = '24px';
+ s.left = '50%';
+ s.marginLeft = -width / 2 + 'px';
+ s.width = width + 'px';
+ s.padding = '24px';
+ s.overflow = 'hidden';
+ s.background = '#fafafa';
+ s.fontFamily = "'Roboto', sans-serif";
+ s.boxShadow = '0px 5px 20px #666';
+ dialog.appendChild(this.createH1_('Select your viewer'));
+ for (var id in options) {
+ dialog.appendChild(this.createChoice_(id, options[id].label));
+ }
+ dialog.appendChild(this.createButton_('Save', this.onSave_.bind(this)));
+ container.appendChild(overlay);
+ container.appendChild(dialog);
+ return container;
+ };
+ ViewerSelector.prototype.createH1_ = function (name) {
+ var h1 = document.createElement('h1');
+ var s = h1.style;
+ s.color = 'black';
+ s.fontSize = '20px';
+ s.fontWeight = 'bold';
+ s.marginTop = 0;
+ s.marginBottom = '24px';
+ h1.innerHTML = name;
+ return h1;
+ };
+ ViewerSelector.prototype.createChoice_ = function (id, name) {
+ var div = document.createElement('div');
+ div.style.marginTop = '8px';
+ div.style.color = 'black';
+ var input = document.createElement('input');
+ input.style.fontSize = '30px';
+ input.setAttribute('id', id);
+ input.setAttribute('type', 'radio');
+ input.setAttribute('value', id);
+ input.setAttribute('name', 'field');
+ var label = document.createElement('label');
+ label.style.marginLeft = '4px';
+ label.setAttribute('for', id);
+ label.innerHTML = name;
+ div.appendChild(input);
+ div.appendChild(label);
+ return div;
+ };
+ ViewerSelector.prototype.createButton_ = function (label, onclick) {
+ var button = document.createElement('button');
+ button.innerHTML = label;
+ var s = button.style;
+ s.float = 'right';
+ s.textTransform = 'uppercase';
+ s.color = '#1094f7';
+ s.fontSize = '14px';
+ s.letterSpacing = 0;
+ s.border = 0;
+ s.background = 'none';
+ s.marginTop = '16px';
+ button.addEventListener('click', onclick);
+ return button;
+ };
+ var commonjsGlobal$$1 = typeof window !== 'undefined' ? window : typeof commonjsGlobal$1 !== 'undefined' ? commonjsGlobal$1 : typeof self !== 'undefined' ? self : {};
+ function unwrapExports$$1 (x) {
+ return x && x.__esModule ? x['default'] : x;
+ }
+ function createCommonjsModule$$1(fn, module) {
+ return module = { exports: {} }, fn(module, module.exports), module.exports;
+ }
+ var NoSleep = createCommonjsModule$$1(function (module, exports) {
+ (function webpackUniversalModuleDefinition(root, factory) {
+ module.exports = factory();
+ })(commonjsGlobal$$1, function() {
+ return (function(modules) {
+ var installedModules = {};
+ function __webpack_require__(moduleId) {
+ if(installedModules[moduleId]) {
+ return installedModules[moduleId].exports;
+ }
+ var module = installedModules[moduleId] = {
+ i: moduleId,
+ l: false,
+ exports: {}
+ };
+ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+ module.l = true;
+ return module.exports;
+ }
+ __webpack_require__.m = modules;
+ __webpack_require__.c = installedModules;
+ __webpack_require__.d = function(exports, name, getter) {
+ if(!__webpack_require__.o(exports, name)) {
+ Object.defineProperty(exports, name, {
+ configurable: false,
+ enumerable: true,
+ get: getter
+ });
+ }
+ };
+ __webpack_require__.n = function(module) {
+ var getter = module && module.__esModule ?
+ function getDefault() { return module['default']; } :
+ function getModuleExports() { return module; };
+ __webpack_require__.d(getter, 'a', getter);
+ return getter;
+ };
+ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+ __webpack_require__.p = "";
+ return __webpack_require__(__webpack_require__.s = 0);
+ })
+ ([
+ (function(module, exports, __webpack_require__) {
+ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ var mediaFile = __webpack_require__(1);
+ var oldIOS = typeof navigator !== 'undefined' && parseFloat(('' + (/CPU.*OS ([0-9_]{3,4})[0-9_]{0,1}|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0, ''])[1]).replace('undefined', '3_2').replace('_', '.').replace('_', '')) < 10 && !window.MSStream;
+ var NoSleep = function () {
+ function NoSleep() {
+ _classCallCheck(this, NoSleep);
+ if (oldIOS) {
+ this.noSleepTimer = null;
+ } else {
+ this.noSleepVideo = document.createElement('video');
+ this.noSleepVideo.setAttribute('playsinline', '');
+ this.noSleepVideo.setAttribute('src', mediaFile);
+ this.noSleepVideo.addEventListener('timeupdate', function (e) {
+ if (this.noSleepVideo.currentTime > 0.5) {
+ this.noSleepVideo.currentTime = Math.random();
+ }
+ }.bind(this));
+ }
+ }
+ _createClass(NoSleep, [{
+ key: 'enable',
+ value: function enable() {
+ if (oldIOS) {
+ this.disable();
+ this.noSleepTimer = window.setInterval(function () {
+ window.location.href = '/';
+ window.setTimeout(window.stop, 0);
+ }, 15000);
+ } else {
+ this.noSleepVideo.play();
+ }
+ }
+ }, {
+ key: 'disable',
+ value: function disable() {
+ if (oldIOS) {
+ if (this.noSleepTimer) {
+ window.clearInterval(this.noSleepTimer);
+ this.noSleepTimer = null;
+ }
+ } else {
+ this.noSleepVideo.pause();
+ }
+ }
+ }]);
+ return NoSleep;
+ }();
+ module.exports = NoSleep;
+ }),
+ (function(module, exports, __webpack_require__) {
+ module.exports = 'data:video/mp4;base64,AAAAIGZ0eXBtcDQyAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAACKBtZGF0AAAC8wYF///v3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE0MiByMjQ3OSBkZDc5YTYxIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTEgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MToweDExMSBtZT1oZXggc3VibWU9MiBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0wIHRocmVhZHM9NiBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTMgYl9weXJhbWlkPTIgYl9hZGFwdD0xIGJfYmlhcz0wIGRpcmVjdD0xIHdlaWdodGI9MSBvcGVuX2dvcD0wIHdlaWdodHA9MSBrZXlpbnQ9MzAwIGtleWludF9taW49MzAgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD0xMCByYz1jcmYgbWJ0cmVlPTEgY3JmPTIwLjAgcWNvbXA9MC42MCBxcG1pbj0wIHFwbWF4PTY5IHFwc3RlcD00IHZidl9tYXhyYXRlPTIwMDAwIHZidl9idWZzaXplPTI1MDAwIGNyZl9tYXg9MC4wIG5hbF9ocmQ9bm9uZSBmaWxsZXI9MCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAOWWIhAA3//p+C7v8tDDSTjf97w55i3SbRPO4ZY+hkjD5hbkAkL3zpJ6h/LR1CAABzgB1kqqzUorlhQAAAAxBmiQYhn/+qZYADLgAAAAJQZ5CQhX/AAj5IQADQGgcIQADQGgcAAAACQGeYUQn/wALKCEAA0BoHAAAAAkBnmNEJ/8ACykhAANAaBwhAANAaBwAAAANQZpoNExDP/6plgAMuSEAA0BoHAAAAAtBnoZFESwr/wAI+SEAA0BoHCEAA0BoHAAAAAkBnqVEJ/8ACykhAANAaBwAAAAJAZ6nRCf/AAsoIQADQGgcIQADQGgcAAAADUGarDRMQz/+qZYADLghAANAaBwAAAALQZ7KRRUsK/8ACPkhAANAaBwAAAAJAZ7pRCf/AAsoIQADQGgcIQADQGgcAAAACQGe60Qn/wALKCEAA0BoHAAAAA1BmvA0TEM//qmWAAy5IQADQGgcIQADQGgcAAAAC0GfDkUVLCv/AAj5IQADQGgcAAAACQGfLUQn/wALKSEAA0BoHCEAA0BoHAAAAAkBny9EJ/8ACyghAANAaBwAAAANQZs0NExDP/6plgAMuCEAA0BoHAAAAAtBn1JFFSwr/wAI+SEAA0BoHCEAA0BoHAAAAAkBn3FEJ/8ACyghAANAaBwAAAAJAZ9zRCf/AAsoIQADQGgcIQADQGgcAAAADUGbeDRMQz/+qZYADLkhAANAaBwAAAALQZ+WRRUsK/8ACPghAANAaBwhAANAaBwAAAAJAZ+1RCf/AAspIQADQGgcAAAACQGft0Qn/wALKSEAA0BoHCEAA0BoHAAAAA1Bm7w0TEM//qmWAAy4IQADQGgcAAAAC0Gf2kUVLCv/AAj5IQADQGgcAAAACQGf+UQn/wALKCEAA0BoHCEAA0BoHAAAAAkBn/tEJ/8ACykhAANAaBwAAAANQZvgNExDP/6plgAMuSEAA0BoHCEAA0BoHAAAAAtBnh5FFSwr/wAI+CEAA0BoHAAAAAkBnj1EJ/8ACyghAANAaBwhAANAaBwAAAAJAZ4/RCf/AAspIQADQGgcAAAADUGaJDRMQz/+qZYADLghAANAaBwAAAALQZ5CRRUsK/8ACPkhAANAaBwhAANAaBwAAAAJAZ5hRCf/AAsoIQADQGgcAAAACQGeY0Qn/wALKSEAA0BoHCEAA0BoHAAAAA1Bmmg0TEM//qmWAAy5IQADQGgcAAAAC0GehkUVLCv/AAj5IQADQGgcIQADQGgcAAAACQGepUQn/wALKSEAA0BoHAAAAAkBnqdEJ/8ACyghAANAaBwAAAANQZqsNExDP/6plgAMuCEAA0BoHCEAA0BoHAAAAAtBnspFFSwr/wAI+SEAA0BoHAAAAAkBnulEJ/8ACyghAANAaBwhAANAaBwAAAAJAZ7rRCf/AAsoIQADQGgcAAAADUGa8DRMQz/+qZYADLkhAANAaBwhAANAaBwAAAALQZ8ORRUsK/8ACPkhAANAaBwAAAAJAZ8tRCf/AAspIQADQGgcIQADQGgcAAAACQGfL0Qn/wALKCEAA0BoHAAAAA1BmzQ0TEM//qmWAAy4IQADQGgcAAAAC0GfUkUVLCv/AAj5IQADQGgcIQADQGgcAAAACQGfcUQn/wALKCEAA0BoHAAAAAkBn3NEJ/8ACyghAANAaBwhAANAaBwAAAANQZt4NExC//6plgAMuSEAA0BoHAAAAAtBn5ZFFSwr/wAI+CEAA0BoHCEAA0BoHAAAAAkBn7VEJ/8ACykhAANAaBwAAAAJAZ+3RCf/AAspIQADQGgcAAAADUGbuzRMQn/+nhAAYsAhAANAaBwhAANAaBwAAAAJQZ/aQhP/AAspIQADQGgcAAAACQGf+UQn/wALKCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHAAACiFtb292AAAAbG12aGQAAAAA1YCCX9WAgl8AAAPoAAAH/AABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAGGlvZHMAAAAAEICAgAcAT////v7/AAAF+XRyYWsAAABcdGtoZAAAAAPVgIJf1YCCXwAAAAEAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAygAAAMoAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAB9AAABdwAAEAAAAABXFtZGlhAAAAIG1kaGQAAAAA1YCCX9WAgl8AAV+QAAK/IFXEAAAAAAAtaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAFZpZGVvSGFuZGxlcgAAAAUcbWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAAE3HN0YmwAAACYc3RzZAAAAAAAAAABAAAAiGF2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAygDKAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAyYXZjQwFNQCj/4QAbZ01AKOyho3ySTUBAQFAAAAMAEAAr8gDxgxlgAQAEaO+G8gAAABhzdHRzAAAAAAAAAAEAAAA8AAALuAAAABRzdHNzAAAAAAAAAAEAAAABAAAB8GN0dHMAAAAAAAAAPAAAAAEAABdwAAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAAC7gAAAAAQAAF3AAAAABAAAAAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAEEc3RzegAAAAAAAAAAAAAAPAAAAzQAAAAQAAAADQAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAANAAAADQAAAQBzdGNvAAAAAAAAADwAAAAwAAADZAAAA3QAAAONAAADoAAAA7kAAAPQAAAD6wAAA/4AAAQXAAAELgAABEMAAARcAAAEbwAABIwAAAShAAAEugAABM0AAATkAAAE/wAABRIAAAUrAAAFQgAABV0AAAVwAAAFiQAABaAAAAW1AAAFzgAABeEAAAX+AAAGEwAABiwAAAY/AAAGVgAABnEAAAaEAAAGnQAABrQAAAbPAAAG4gAABvUAAAcSAAAHJwAAB0AAAAdTAAAHcAAAB4UAAAeeAAAHsQAAB8gAAAfjAAAH9gAACA8AAAgmAAAIQQAACFQAAAhnAAAIhAAACJcAAAMsdHJhawAAAFx0a2hkAAAAA9WAgl/VgIJfAAAAAgAAAAAAAAf8AAAAAAAAAAAAAAABAQAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAACsm1kaWEAAAAgbWRoZAAAAADVgIJf1YCCXwAArEQAAWAAVcQAAAAAACdoZGxyAAAAAAAAAABzb3VuAAAAAAAAAAAAAAAAU3RlcmVvAAAAAmNtaW5mAAAAEHNtaGQAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAidzdGJsAAAAZ3N0c2QAAAAAAAAAAQAAAFdtcDRhAAAAAAAAAAEAAAAAAAAAAAACABAAAAAArEQAAAAAADNlc2RzAAAAAAOAgIAiAAIABICAgBRAFQAAAAADDUAAAAAABYCAgAISEAaAgIABAgAAABhzdHRzAAAAAAAAAAEAAABYAAAEAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAUc3RzegAAAAAAAAAGAAAAWAAAAXBzdGNvAAAAAAAAAFgAAAOBAAADhwAAA5oAAAOtAAADswAAA8oAAAPfAAAD5QAAA/gAAAQLAAAEEQAABCgAAAQ9AAAEUAAABFYAAARpAAAEgAAABIYAAASbAAAErgAABLQAAATHAAAE3gAABPMAAAT5AAAFDAAABR8AAAUlAAAFPAAABVEAAAVXAAAFagAABX0AAAWDAAAFmgAABa8AAAXCAAAFyAAABdsAAAXyAAAF+AAABg0AAAYgAAAGJgAABjkAAAZQAAAGZQAABmsAAAZ+AAAGkQAABpcAAAauAAAGwwAABskAAAbcAAAG7wAABwYAAAcMAAAHIQAABzQAAAc6AAAHTQAAB2QAAAdqAAAHfwAAB5IAAAeYAAAHqwAAB8IAAAfXAAAH3QAAB/AAAAgDAAAICQAACCAAAAg1AAAIOwAACE4AAAhhAAAIeAAACH4AAAiRAAAIpAAACKoAAAiwAAAItgAACLwAAAjCAAAAFnVkdGEAAAAObmFtZVN0ZXJlbwAAAHB1ZHRhAAAAaG1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAAO2lsc3QAAAAzqXRvbwAAACtkYXRhAAAAAQAAAABIYW5kQnJha2UgMC4xMC4yIDIwMTUwNjExMDA=';
+ })
+ ]);
+ });
+ });
+ var NoSleep$1 = unwrapExports$$1(NoSleep);
+ var nextDisplayId = 1000;
+ var defaultLeftBounds = [0, 0, 0.5, 1];
+ var defaultRightBounds = [0.5, 0, 0.5, 1];
+ var raf = window.requestAnimationFrame;
+ var caf = window.cancelAnimationFrame;
+ function VRFrameData() {
+ this.leftProjectionMatrix = new Float32Array(16);
+ this.leftViewMatrix = new Float32Array(16);
+ this.rightProjectionMatrix = new Float32Array(16);
+ this.rightViewMatrix = new Float32Array(16);
+ this.pose = null;
+ }
+ function VRDisplayCapabilities(config) {
+ Object.defineProperties(this, {
+ hasPosition: {
+ writable: false, enumerable: true, value: config.hasPosition
+ },
+ hasExternalDisplay: {
+ writable: false, enumerable: true, value: config.hasExternalDisplay
+ },
+ canPresent: {
+ writable: false, enumerable: true, value: config.canPresent
+ },
+ maxLayers: {
+ writable: false, enumerable: true, value: config.maxLayers
+ },
+ hasOrientation: {
+ enumerable: true, get: function get() {
+ deprecateWarning('VRDisplayCapabilities.prototype.hasOrientation', 'VRDisplay.prototype.getFrameData');
+ return config.hasOrientation;
+ }
+ }
+ });
+ }
+ function VRDisplay(config) {
+ config = config || {};
+ var USE_WAKELOCK = 'wakelock' in config ? config.wakelock : true;
+ this.isPolyfilled = true;
+ this.displayId = nextDisplayId++;
+ this.displayName = '';
+ this.depthNear = 0.01;
+ this.depthFar = 10000.0;
+ this.isPresenting = false;
+ Object.defineProperty(this, 'isConnected', {
+ get: function get() {
+ deprecateWarning('VRDisplay.prototype.isConnected', 'VRDisplayCapabilities.prototype.hasExternalDisplay');
+ return false;
+ }
+ });
+ this.capabilities = new VRDisplayCapabilities({
+ hasPosition: false,
+ hasOrientation: false,
+ hasExternalDisplay: false,
+ canPresent: false,
+ maxLayers: 1
+ });
+ this.stageParameters = null;
+ this.waitingForPresent_ = false;
+ this.layer_ = null;
+ this.originalParent_ = null;
+ this.fullscreenElement_ = null;
+ this.fullscreenWrapper_ = null;
+ this.fullscreenElementCachedStyle_ = null;
+ this.fullscreenEventTarget_ = null;
+ this.fullscreenChangeHandler_ = null;
+ this.fullscreenErrorHandler_ = null;
+ if (USE_WAKELOCK && isMobile()) {
+ this.wakelock_ = new NoSleep$1();
+ }
+ }
+ VRDisplay.prototype.getFrameData = function (frameData) {
+ return frameDataFromPose(frameData, this._getPose(), this);
+ };
+ VRDisplay.prototype.getPose = function () {
+ deprecateWarning('VRDisplay.prototype.getPose', 'VRDisplay.prototype.getFrameData');
+ return this._getPose();
+ };
+ VRDisplay.prototype.resetPose = function () {
+ deprecateWarning('VRDisplay.prototype.resetPose');
+ return this._resetPose();
+ };
+ VRDisplay.prototype.getImmediatePose = function () {
+ deprecateWarning('VRDisplay.prototype.getImmediatePose', 'VRDisplay.prototype.getFrameData');
+ return this._getPose();
+ };
+ VRDisplay.prototype.requestAnimationFrame = function (callback) {
+ return raf(callback);
+ };
+ VRDisplay.prototype.cancelAnimationFrame = function (id) {
+ return caf(id);
+ };
+ VRDisplay.prototype.wrapForFullscreen = function (element) {
+ if (isIOS()) {
+ return element;
+ }
+ if (!this.fullscreenWrapper_) {
+ this.fullscreenWrapper_ = document.createElement('div');
+ var cssProperties = ['height: ' + Math.min(screen.height, screen.width) + 'px !important', 'top: 0 !important', 'left: 0 !important', 'right: 0 !important', 'border: 0', 'margin: 0', 'padding: 0', 'z-index: 999999 !important', 'position: fixed'];
+ this.fullscreenWrapper_.setAttribute('style', cssProperties.join('; ') + ';');
+ this.fullscreenWrapper_.classList.add('webvr-polyfill-fullscreen-wrapper');
+ }
+ if (this.fullscreenElement_ == element) {
+ return this.fullscreenWrapper_;
+ }
+ if (this.fullscreenElement_) {
+ if (this.originalParent_) {
+ this.originalParent_.appendChild(this.fullscreenElement_);
+ } else {
+ this.fullscreenElement_.parentElement.removeChild(this.fullscreenElement_);
+ }
+ }
+ this.fullscreenElement_ = element;
+ this.originalParent_ = element.parentElement;
+ if (!this.originalParent_) {
+ document.body.appendChild(element);
+ }
+ if (!this.fullscreenWrapper_.parentElement) {
+ var parent = this.fullscreenElement_.parentElement;
+ parent.insertBefore(this.fullscreenWrapper_, this.fullscreenElement_);
+ parent.removeChild(this.fullscreenElement_);
+ }
+ this.fullscreenWrapper_.insertBefore(this.fullscreenElement_, this.fullscreenWrapper_.firstChild);
+ this.fullscreenElementCachedStyle_ = this.fullscreenElement_.getAttribute('style');
+ var self = this;
+ function applyFullscreenElementStyle() {
+ if (!self.fullscreenElement_) {
+ return;
+ }
+ var cssProperties = ['position: absolute', 'top: 0', 'left: 0', 'width: ' + Math.max(screen.width, screen.height) + 'px', 'height: ' + Math.min(screen.height, screen.width) + 'px', 'border: 0', 'margin: 0', 'padding: 0'];
+ self.fullscreenElement_.setAttribute('style', cssProperties.join('; ') + ';');
+ }
+ applyFullscreenElementStyle();
+ return this.fullscreenWrapper_;
+ };
+ VRDisplay.prototype.removeFullscreenWrapper = function () {
+ if (!this.fullscreenElement_) {
+ return;
+ }
+ var element = this.fullscreenElement_;
+ if (this.fullscreenElementCachedStyle_) {
+ element.setAttribute('style', this.fullscreenElementCachedStyle_);
+ } else {
+ element.removeAttribute('style');
+ }
+ this.fullscreenElement_ = null;
+ this.fullscreenElementCachedStyle_ = null;
+ var parent = this.fullscreenWrapper_.parentElement;
+ this.fullscreenWrapper_.removeChild(element);
+ if (this.originalParent_ === parent) {
+ parent.insertBefore(element, this.fullscreenWrapper_);
+ }
+ else if (this.originalParent_) {
+ this.originalParent_.appendChild(element);
+ }
+ parent.removeChild(this.fullscreenWrapper_);
+ return element;
+ };
+ VRDisplay.prototype.requestPresent = function (layers) {
+ var wasPresenting = this.isPresenting;
+ var self = this;
+ if (!(layers instanceof Array)) {
+ deprecateWarning('VRDisplay.prototype.requestPresent with non-array argument', 'an array of VRLayers as the first argument');
+ layers = [layers];
+ }
+ return new Promise(function (resolve, reject) {
+ if (!self.capabilities.canPresent) {
+ reject(new Error('VRDisplay is not capable of presenting.'));
+ return;
+ }
+ if (layers.length == 0 || layers.length > self.capabilities.maxLayers) {
+ reject(new Error('Invalid number of layers.'));
+ return;
+ }
+ var incomingLayer = layers[0];
+ if (!incomingLayer.source) {
+ resolve();
+ return;
+ }
+ var leftBounds = incomingLayer.leftBounds || defaultLeftBounds;
+ var rightBounds = incomingLayer.rightBounds || defaultRightBounds;
+ if (wasPresenting) {
+ var layer = self.layer_;
+ if (layer.source !== incomingLayer.source) {
+ layer.source = incomingLayer.source;
+ }
+ for (var i = 0; i < 4; i++) {
+ layer.leftBounds[i] = leftBounds[i];
+ layer.rightBounds[i] = rightBounds[i];
+ }
+ self.wrapForFullscreen(self.layer_.source);
+ self.updatePresent_();
+ resolve();
+ return;
+ }
+ self.layer_ = {
+ predistorted: incomingLayer.predistorted,
+ source: incomingLayer.source,
+ leftBounds: leftBounds.slice(0),
+ rightBounds: rightBounds.slice(0)
+ };
+ self.waitingForPresent_ = false;
+ if (self.layer_ && self.layer_.source) {
+ var fullscreenElement = self.wrapForFullscreen(self.layer_.source);
+ var onFullscreenChange = function onFullscreenChange() {
+ var actualFullscreenElement = getFullscreenElement();
+ self.isPresenting = fullscreenElement === actualFullscreenElement;
+ if (self.isPresenting) {
+ if (screen.orientation && screen.orientation.lock) {
+ screen.orientation.lock('landscape-primary').catch(function (error) {
+ console.error('screen.orientation.lock() failed due to', error.message);
+ });
+ }
+ self.waitingForPresent_ = false;
+ self.beginPresent_();
+ resolve();
+ } else {
+ if (screen.orientation && screen.orientation.unlock) {
+ screen.orientation.unlock();
+ }
+ self.removeFullscreenWrapper();
+ self.disableWakeLock();
+ self.endPresent_();
+ self.removeFullscreenListeners_();
+ }
+ self.fireVRDisplayPresentChange_();
+ };
+ var onFullscreenError = function onFullscreenError() {
+ if (!self.waitingForPresent_) {
+ return;
+ }
+ self.removeFullscreenWrapper();
+ self.removeFullscreenListeners_();
+ self.disableWakeLock();
+ self.waitingForPresent_ = false;
+ self.isPresenting = false;
+ reject(new Error('Unable to present.'));
+ };
+ self.addFullscreenListeners_(fullscreenElement, onFullscreenChange, onFullscreenError);
+ if (requestFullscreen(fullscreenElement)) {
+ self.enableWakeLock();
+ self.waitingForPresent_ = true;
+ } else if (isIOS() || isWebViewAndroid()) {
+ self.enableWakeLock();
+ self.isPresenting = true;
+ self.beginPresent_();
+ self.fireVRDisplayPresentChange_();
+ resolve();
+ }
+ }
+ if (!self.waitingForPresent_ && !isIOS()) {
+ exitFullscreen();
+ reject(new Error('Unable to present.'));
+ }
+ });
+ };
+ VRDisplay.prototype.exitPresent = function () {
+ var wasPresenting = this.isPresenting;
+ var self = this;
+ this.isPresenting = false;
+ this.layer_ = null;
+ this.disableWakeLock();
+ return new Promise(function (resolve, reject) {
+ if (wasPresenting) {
+ if (!exitFullscreen() && isIOS()) {
+ self.endPresent_();
+ self.fireVRDisplayPresentChange_();
+ }
+ if (isWebViewAndroid()) {
+ self.removeFullscreenWrapper();
+ self.removeFullscreenListeners_();
+ self.endPresent_();
+ self.fireVRDisplayPresentChange_();
+ }
+ resolve();
+ } else {
+ reject(new Error('Was not presenting to VRDisplay.'));
+ }
+ });
+ };
+ VRDisplay.prototype.getLayers = function () {
+ if (this.layer_) {
+ return [this.layer_];
+ }
+ return [];
+ };
+ VRDisplay.prototype.fireVRDisplayPresentChange_ = function () {
+ var event = new CustomEvent('vrdisplaypresentchange', { detail: { display: this } });
+ window.dispatchEvent(event);
+ };
+ VRDisplay.prototype.fireVRDisplayConnect_ = function () {
+ var event = new CustomEvent('vrdisplayconnect', { detail: { display: this } });
+ window.dispatchEvent(event);
+ };
+ VRDisplay.prototype.addFullscreenListeners_ = function (element, changeHandler, errorHandler) {
+ this.removeFullscreenListeners_();
+ this.fullscreenEventTarget_ = element;
+ this.fullscreenChangeHandler_ = changeHandler;
+ this.fullscreenErrorHandler_ = errorHandler;
+ if (changeHandler) {
+ if (document.fullscreenEnabled) {
+ element.addEventListener('fullscreenchange', changeHandler, false);
+ } else if (document.webkitFullscreenEnabled) {
+ element.addEventListener('webkitfullscreenchange', changeHandler, false);
+ } else if (document.mozFullScreenEnabled) {
+ document.addEventListener('mozfullscreenchange', changeHandler, false);
+ } else if (document.msFullscreenEnabled) {
+ element.addEventListener('msfullscreenchange', changeHandler, false);
+ }
+ }
+ if (errorHandler) {
+ if (document.fullscreenEnabled) {
+ element.addEventListener('fullscreenerror', errorHandler, false);
+ } else if (document.webkitFullscreenEnabled) {
+ element.addEventListener('webkitfullscreenerror', errorHandler, false);
+ } else if (document.mozFullScreenEnabled) {
+ document.addEventListener('mozfullscreenerror', errorHandler, false);
+ } else if (document.msFullscreenEnabled) {
+ element.addEventListener('msfullscreenerror', errorHandler, false);
+ }
+ }
+ };
+ VRDisplay.prototype.removeFullscreenListeners_ = function () {
+ if (!this.fullscreenEventTarget_) return;
+ var element = this.fullscreenEventTarget_;
+ if (this.fullscreenChangeHandler_) {
+ var changeHandler = this.fullscreenChangeHandler_;
+ element.removeEventListener('fullscreenchange', changeHandler, false);
+ element.removeEventListener('webkitfullscreenchange', changeHandler, false);
+ document.removeEventListener('mozfullscreenchange', changeHandler, false);
+ element.removeEventListener('msfullscreenchange', changeHandler, false);
+ }
+ if (this.fullscreenErrorHandler_) {
+ var errorHandler = this.fullscreenErrorHandler_;
+ element.removeEventListener('fullscreenerror', errorHandler, false);
+ element.removeEventListener('webkitfullscreenerror', errorHandler, false);
+ document.removeEventListener('mozfullscreenerror', errorHandler, false);
+ element.removeEventListener('msfullscreenerror', errorHandler, false);
+ }
+ this.fullscreenEventTarget_ = null;
+ this.fullscreenChangeHandler_ = null;
+ this.fullscreenErrorHandler_ = null;
+ };
+ VRDisplay.prototype.enableWakeLock = function () {
+ if (this.wakelock_) {
+ this.wakelock_.enable();
+ }
+ };
+ VRDisplay.prototype.disableWakeLock = function () {
+ if (this.wakelock_) {
+ this.wakelock_.disable();
+ }
+ };
+ VRDisplay.prototype.beginPresent_ = function () {
+ };
+ VRDisplay.prototype.endPresent_ = function () {
+ };
+ VRDisplay.prototype.submitFrame = function (pose) {
+ };
+ VRDisplay.prototype.getEyeParameters = function (whichEye) {
+ return null;
+ };
+ var config = {
+ ADDITIONAL_VIEWERS: [],
+ DEFAULT_VIEWER: '',
+ MOBILE_WAKE_LOCK: true,
+ DEBUG: false,
+ DPDB_URL: 'https://dpdb.webvr.rocks/dpdb.json',
+ K_FILTER: 0.98,
+ PREDICTION_TIME_S: 0.040,
+ CARDBOARD_UI_DISABLED: false,
+ ROTATE_INSTRUCTIONS_DISABLED: false,
+ YAW_ONLY: false,
+ BUFFER_SCALE: 0.5,
+ DIRTY_SUBMIT_FRAME_BINDINGS: false
+ };
+ var Eye = {
+ LEFT: 'left',
+ RIGHT: 'right'
+ };
+ function CardboardVRDisplay(config$$1) {
+ var defaults = extend({}, config);
+ config$$1 = extend(defaults, config$$1 || {});
+ VRDisplay.call(this, {
+ wakelock: config$$1.MOBILE_WAKE_LOCK
+ });
+ this.config = config$$1;
+ this.displayName = 'Cardboard VRDisplay';
+ this.capabilities = new VRDisplayCapabilities({
+ hasPosition: false,
+ hasOrientation: true,
+ hasExternalDisplay: false,
+ canPresent: true,
+ maxLayers: 1
+ });
+ this.stageParameters = null;
+ this.bufferScale_ = this.config.BUFFER_SCALE;
+ this.poseSensor_ = new PoseSensor(this.config);
+ this.distorter_ = null;
+ this.cardboardUI_ = null;
+ this.dpdb_ = new Dpdb(this.config.DPDB_URL, this.onDeviceParamsUpdated_.bind(this));
+ this.deviceInfo_ = new DeviceInfo(this.dpdb_.getDeviceParams(), config$$1.ADDITIONAL_VIEWERS);
+ this.viewerSelector_ = new ViewerSelector(config$$1.DEFAULT_VIEWER);
+ this.viewerSelector_.onChange(this.onViewerChanged_.bind(this));
+ this.deviceInfo_.setViewer(this.viewerSelector_.getCurrentViewer());
+ if (!this.config.ROTATE_INSTRUCTIONS_DISABLED) {
+ this.rotateInstructions_ = new RotateInstructions();
+ }
+ if (isIOS()) {
+ window.addEventListener('resize', this.onResize_.bind(this));
+ }
+ }
+ CardboardVRDisplay.prototype = Object.create(VRDisplay.prototype);
+ CardboardVRDisplay.prototype._getPose = function () {
+ return {
+ position: null,
+ orientation: this.poseSensor_.getOrientation(),
+ linearVelocity: null,
+ linearAcceleration: null,
+ angularVelocity: null,
+ angularAcceleration: null
+ };
+ };
+ CardboardVRDisplay.prototype._resetPose = function () {
+ if (this.poseSensor_.resetPose) {
+ this.poseSensor_.resetPose();
+ }
+ };
+ CardboardVRDisplay.prototype._getFieldOfView = function (whichEye) {
+ var fieldOfView;
+ if (whichEye == Eye.LEFT) {
+ fieldOfView = this.deviceInfo_.getFieldOfViewLeftEye();
+ } else if (whichEye == Eye.RIGHT) {
+ fieldOfView = this.deviceInfo_.getFieldOfViewRightEye();
+ } else {
+ console.error('Invalid eye provided: %s', whichEye);
+ return null;
+ }
+ return fieldOfView;
+ };
+ CardboardVRDisplay.prototype._getEyeOffset = function (whichEye) {
+ var offset;
+ if (whichEye == Eye.LEFT) {
+ offset = [-this.deviceInfo_.viewer.interLensDistance * 0.5, 0.0, 0.0];
+ } else if (whichEye == Eye.RIGHT) {
+ offset = [this.deviceInfo_.viewer.interLensDistance * 0.5, 0.0, 0.0];
+ } else {
+ console.error('Invalid eye provided: %s', whichEye);
+ return null;
+ }
+ return offset;
+ };
+ CardboardVRDisplay.prototype.getEyeParameters = function (whichEye) {
+ var offset = this._getEyeOffset(whichEye);
+ var fieldOfView = this._getFieldOfView(whichEye);
+ var eyeParams = {
+ offset: offset,
+ renderWidth: this.deviceInfo_.device.width * 0.5 * this.bufferScale_,
+ renderHeight: this.deviceInfo_.device.height * this.bufferScale_
+ };
+ Object.defineProperty(eyeParams, 'fieldOfView', {
+ enumerable: true,
+ get: function get() {
+ deprecateWarning('VRFieldOfView', 'VRFrameData\'s projection matrices');
+ return fieldOfView;
+ }
+ });
+ return eyeParams;
+ };
+ CardboardVRDisplay.prototype.onDeviceParamsUpdated_ = function (newParams) {
+ if (this.config.DEBUG) {
+ console.log('DPDB reported that device params were updated.');
+ }
+ this.deviceInfo_.updateDeviceParams(newParams);
+ if (this.distorter_) {
+ this.distorter_.updateDeviceInfo(this.deviceInfo_);
+ }
+ };
+ CardboardVRDisplay.prototype.updateBounds_ = function () {
+ if (this.layer_ && this.distorter_ && (this.layer_.leftBounds || this.layer_.rightBounds)) {
+ this.distorter_.setTextureBounds(this.layer_.leftBounds, this.layer_.rightBounds);
+ }
+ };
+ CardboardVRDisplay.prototype.beginPresent_ = function () {
+ var gl = this.layer_.source.getContext('webgl');
+ if (!gl) gl = this.layer_.source.getContext('experimental-webgl');
+ if (!gl) gl = this.layer_.source.getContext('webgl2');
+ if (!gl) return;
+ if (this.layer_.predistorted) {
+ if (!this.config.CARDBOARD_UI_DISABLED) {
+ gl.canvas.width = getScreenWidth() * this.bufferScale_;
+ gl.canvas.height = getScreenHeight() * this.bufferScale_;
+ this.cardboardUI_ = new CardboardUI(gl);
+ }
+ } else {
+ if (!this.config.CARDBOARD_UI_DISABLED) {
+ this.cardboardUI_ = new CardboardUI(gl);
+ }
+ this.distorter_ = new CardboardDistorter(gl, this.cardboardUI_, this.config.BUFFER_SCALE, this.config.DIRTY_SUBMIT_FRAME_BINDINGS);
+ this.distorter_.updateDeviceInfo(this.deviceInfo_);
+ }
+ if (this.cardboardUI_) {
+ this.cardboardUI_.listen(function (e) {
+ this.viewerSelector_.show(this.layer_.source.parentElement);
+ e.stopPropagation();
+ e.preventDefault();
+ }.bind(this), function (e) {
+ this.exitPresent();
+ e.stopPropagation();
+ e.preventDefault();
+ }.bind(this));
+ }
+ if (this.rotateInstructions_) {
+ if (isLandscapeMode() && isMobile()) {
+ this.rotateInstructions_.showTemporarily(3000, this.layer_.source.parentElement);
+ } else {
+ this.rotateInstructions_.update();
+ }
+ }
+ this.orientationHandler = this.onOrientationChange_.bind(this);
+ window.addEventListener('orientationchange', this.orientationHandler);
+ this.vrdisplaypresentchangeHandler = this.updateBounds_.bind(this);
+ window.addEventListener('vrdisplaypresentchange', this.vrdisplaypresentchangeHandler);
+ this.fireVRDisplayDeviceParamsChange_();
+ };
+ CardboardVRDisplay.prototype.endPresent_ = function () {
+ if (this.distorter_) {
+ this.distorter_.destroy();
+ this.distorter_ = null;
+ }
+ if (this.cardboardUI_) {
+ this.cardboardUI_.destroy();
+ this.cardboardUI_ = null;
+ }
+ if (this.rotateInstructions_) {
+ this.rotateInstructions_.hide();
+ }
+ this.viewerSelector_.hide();
+ window.removeEventListener('orientationchange', this.orientationHandler);
+ window.removeEventListener('vrdisplaypresentchange', this.vrdisplaypresentchangeHandler);
+ };
+ CardboardVRDisplay.prototype.updatePresent_ = function () {
+ this.endPresent_();
+ this.beginPresent_();
+ };
+ CardboardVRDisplay.prototype.submitFrame = function (pose) {
+ if (this.distorter_) {
+ this.updateBounds_();
+ this.distorter_.submitFrame();
+ } else if (this.cardboardUI_ && this.layer_) {
+ var canvas = this.layer_.source.getContext('webgl').canvas;
+ if (canvas.width != this.lastWidth || canvas.height != this.lastHeight) {
+ this.cardboardUI_.onResize();
+ }
+ this.lastWidth = canvas.width;
+ this.lastHeight = canvas.height;
+ this.cardboardUI_.render();
+ }
+ };
+ CardboardVRDisplay.prototype.onOrientationChange_ = function (e) {
+ this.viewerSelector_.hide();
+ if (this.rotateInstructions_) {
+ this.rotateInstructions_.update();
+ }
+ this.onResize_();
+ };
+ CardboardVRDisplay.prototype.onResize_ = function (e) {
+ if (this.layer_) {
+ var gl = this.layer_.source.getContext('webgl');
+ var cssProperties = ['position: absolute', 'top: 0', 'left: 0',
+ 'width: 100vw', 'height: 100vh', 'border: 0', 'margin: 0',
+ 'padding: 0px', 'box-sizing: content-box'];
+ gl.canvas.setAttribute('style', cssProperties.join('; ') + ';');
+ safariCssSizeWorkaround(gl.canvas);
+ }
+ };
+ CardboardVRDisplay.prototype.onViewerChanged_ = function (viewer) {
+ this.deviceInfo_.setViewer(viewer);
+ if (this.distorter_) {
+ this.distorter_.updateDeviceInfo(this.deviceInfo_);
+ }
+ this.fireVRDisplayDeviceParamsChange_();
+ };
+ CardboardVRDisplay.prototype.fireVRDisplayDeviceParamsChange_ = function () {
+ var event = new CustomEvent('vrdisplaydeviceparamschange', {
+ detail: {
+ vrdisplay: this,
+ deviceInfo: this.deviceInfo_
+ }
+ });
+ window.dispatchEvent(event);
+ };
+ CardboardVRDisplay.VRFrameData = VRFrameData;
+ CardboardVRDisplay.VRDisplay = VRDisplay;
+ return CardboardVRDisplay;
+ })));
+ });
+ var CardboardVRDisplay = unwrapExports(cardboardVrDisplay);
+
+ var version = "0.10.6";
+
+ var DefaultConfig = {
+ ADDITIONAL_VIEWERS: [],
+ DEFAULT_VIEWER: '',
+ PROVIDE_MOBILE_VRDISPLAY: true,
+ GET_VR_DISPLAYS_TIMEOUT: 1000,
+ MOBILE_WAKE_LOCK: true,
+ DEBUG: false,
+ DPDB_URL: 'https://dpdb.webvr.rocks/dpdb.json',
+ K_FILTER: 0.98,
+ PREDICTION_TIME_S: 0.040,
+ TOUCH_PANNER_DISABLED: true,
+ CARDBOARD_UI_DISABLED: false,
+ ROTATE_INSTRUCTIONS_DISABLED: false,
+ YAW_ONLY: false,
+ BUFFER_SCALE: 0.5,
+ DIRTY_SUBMIT_FRAME_BINDINGS: false
+ };
+
+ function WebVRPolyfill(config) {
+ this.config = extend(extend({}, DefaultConfig), config);
+ this.polyfillDisplays = [];
+ this.enabled = false;
+ this.hasNative = 'getVRDisplays' in navigator;
+ this.native = {};
+ this.native.getVRDisplays = navigator.getVRDisplays;
+ this.native.VRFrameData = window.VRFrameData;
+ this.native.VRDisplay = window.VRDisplay;
+ if (!this.hasNative || this.config.PROVIDE_MOBILE_VRDISPLAY && isMobile()) {
+ this.enable();
+ this.getVRDisplays().then(function (displays) {
+ if (displays && displays[0] && displays[0].fireVRDisplayConnect_) {
+ displays[0].fireVRDisplayConnect_();
+ }
+ });
+ }
+ }
+ WebVRPolyfill.prototype.getPolyfillDisplays = function () {
+ if (this._polyfillDisplaysPopulated) {
+ return this.polyfillDisplays;
+ }
+ if (isMobile()) {
+ var vrDisplay = new CardboardVRDisplay({
+ ADDITIONAL_VIEWERS: this.config.ADDITIONAL_VIEWERS,
+ DEFAULT_VIEWER: this.config.DEFAULT_VIEWER,
+ MOBILE_WAKE_LOCK: this.config.MOBILE_WAKE_LOCK,
+ DEBUG: this.config.DEBUG,
+ DPDB_URL: this.config.DPDB_URL,
+ CARDBOARD_UI_DISABLED: this.config.CARDBOARD_UI_DISABLED,
+ K_FILTER: this.config.K_FILTER,
+ PREDICTION_TIME_S: this.config.PREDICTION_TIME_S,
+ TOUCH_PANNER_DISABLED: this.config.TOUCH_PANNER_DISABLED,
+ ROTATE_INSTRUCTIONS_DISABLED: this.config.ROTATE_INSTRUCTIONS_DISABLED,
+ YAW_ONLY: this.config.YAW_ONLY,
+ BUFFER_SCALE: this.config.BUFFER_SCALE,
+ DIRTY_SUBMIT_FRAME_BINDINGS: this.config.DIRTY_SUBMIT_FRAME_BINDINGS
+ });
+ this.polyfillDisplays.push(vrDisplay);
+ }
+ this._polyfillDisplaysPopulated = true;
+ return this.polyfillDisplays;
+ };
+ WebVRPolyfill.prototype.enable = function () {
+ this.enabled = true;
+ if (this.hasNative && this.native.VRFrameData) {
+ var NativeVRFrameData = this.native.VRFrameData;
+ var nativeFrameData = new this.native.VRFrameData();
+ var nativeGetFrameData = this.native.VRDisplay.prototype.getFrameData;
+ window.VRDisplay.prototype.getFrameData = function (frameData) {
+ if (frameData instanceof NativeVRFrameData) {
+ nativeGetFrameData.call(this, frameData);
+ return;
+ }
+ nativeGetFrameData.call(this, nativeFrameData);
+ frameData.pose = nativeFrameData.pose;
+ copyArray(nativeFrameData.leftProjectionMatrix, frameData.leftProjectionMatrix);
+ copyArray(nativeFrameData.rightProjectionMatrix, frameData.rightProjectionMatrix);
+ copyArray(nativeFrameData.leftViewMatrix, frameData.leftViewMatrix);
+ copyArray(nativeFrameData.rightViewMatrix, frameData.rightViewMatrix);
+ };
+ }
+ navigator.getVRDisplays = this.getVRDisplays.bind(this);
+ window.VRDisplay = CardboardVRDisplay.VRDisplay;
+ window.VRFrameData = CardboardVRDisplay.VRFrameData;
+ };
+ WebVRPolyfill.prototype.getVRDisplays = function () {
+ var _this = this;
+ var config = this.config;
+ if (!this.hasNative) {
+ return Promise.resolve(this.getPolyfillDisplays());
+ }
+ var timeoutId;
+ var vrDisplaysNative = this.native.getVRDisplays.call(navigator);
+ var timeoutPromise = new Promise(function (resolve) {
+ timeoutId = setTimeout(function () {
+ console.warn('Native WebVR implementation detected, but `getVRDisplays()` failed to resolve. Falling back to polyfill.');
+ resolve([]);
+ }, config.GET_VR_DISPLAYS_TIMEOUT);
+ });
+ return race([vrDisplaysNative, timeoutPromise]).then(function (nativeDisplays) {
+ clearTimeout(timeoutId);
+ return nativeDisplays.length > 0 ? nativeDisplays : _this.getPolyfillDisplays();
+ });
+ };
+ WebVRPolyfill.version = version;
+ WebVRPolyfill.VRFrameData = CardboardVRDisplay.VRFrameData;
+ WebVRPolyfill.VRDisplay = CardboardVRDisplay.VRDisplay;
+
+
+ var webvrPolyfill = Object.freeze({
+ default: WebVRPolyfill
+ });
+
+ var require$$0 = ( webvrPolyfill && WebVRPolyfill ) || webvrPolyfill;
+
+ if (typeof commonjsGlobal$1 !== 'undefined' && commonjsGlobal$1.window) {
+ if (!commonjsGlobal$1.document) {
+ commonjsGlobal$1.document = commonjsGlobal$1.window.document;
+ }
+ if (!commonjsGlobal$1.navigator) {
+ commonjsGlobal$1.navigator = commonjsGlobal$1.window.navigator;
+ }
+ }
+ var src = require$$0;
+
+ return src;
+
+ })));
+ });
+
+ var WebVRPolyfill = unwrapExports(webvrPolyfill);
+
+ // Polyfills
+
+ if ( Number.EPSILON === undefined ) {
+
+ Number.EPSILON = Math.pow( 2, - 52 );
+
+ }
+
+ if ( Number.isInteger === undefined ) {
+
+ // Missing in IE
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
+
+ Number.isInteger = function ( value ) {
+
+ return typeof value === 'number' && isFinite( value ) && Math.floor( value ) === value;
+
+ };
+
+ }
+
+ //
+
+ if ( Math.sign === undefined ) {
+
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
+
+ Math.sign = function ( x ) {
+
+ return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x;
+
+ };
+
+ }
+
+ if ( 'name' in Function.prototype === false ) {
+
+ // Missing in IE
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
+
+ Object.defineProperty( Function.prototype, 'name', {
+
+ get: function () {
+
+ return this.toString().match( /^\s*function\s*([^\(\s]*)/ )[ 1 ];
+
+ }
+
+ } );
+
+ }
+
+ if ( Object.assign === undefined ) {
+
+ // Missing in IE
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
+
+ ( function () {
+
+ Object.assign = function ( target ) {
+
+ if ( target === undefined || target === null ) {
+
+ throw new TypeError( 'Cannot convert undefined or null to object' );
+
+ }
+
+ var output = Object( target );
+
+ for ( var index = 1; index < arguments.length; index ++ ) {
+
+ var source = arguments[ index ];
+
+ if ( source !== undefined && source !== null ) {
+
+ for ( var nextKey in source ) {
+
+ if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) {
+
+ output[ nextKey ] = source[ nextKey ];
+
+ }
+
+ }
+
+ }
+
+ }
+
+ return output;
+
+ };
+
+ } )();
+
+ }
+
+ /**
+ * https://github.com/mrdoob/eventdispatcher.js/
+ */
+
+ function EventDispatcher() {}
+
+ Object.assign( EventDispatcher.prototype, {
+
+ addEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) this._listeners = {};
+
+ var listeners = this._listeners;
+
+ if ( listeners[ type ] === undefined ) {
+
+ listeners[ type ] = [];
+
+ }
+
+ if ( listeners[ type ].indexOf( listener ) === - 1 ) {
+
+ listeners[ type ].push( listener );
+
+ }
+
+ },
+
+ hasEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) return false;
+
+ var listeners = this._listeners;
+
+ return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1;
+
+ },
+
+ removeEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) return;
+
+ var listeners = this._listeners;
+ var listenerArray = listeners[ type ];
+
+ if ( listenerArray !== undefined ) {
+
+ var index = listenerArray.indexOf( listener );
+
+ if ( index !== - 1 ) {
+
+ listenerArray.splice( index, 1 );
+
+ }
+
+ }
+
+ },
+
+ dispatchEvent: function ( event ) {
+
+ if ( this._listeners === undefined ) return;
+
+ var listeners = this._listeners;
+ var listenerArray = listeners[ event.type ];
+
+ if ( listenerArray !== undefined ) {
+
+ event.target = this;
+
+ var array = listenerArray.slice( 0 );
+
+ for ( var i = 0, l = array.length; i < l; i ++ ) {
+
+ array[ i ].call( this, event );
+
+ }
+
+ }
+
+ }
+
+ } );
+
+ var REVISION = '93';
+ var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
+ var CullFaceNone = 0;
+ var CullFaceBack = 1;
+ var CullFaceFront = 2;
+ var PCFShadowMap = 1;
+ var PCFSoftShadowMap = 2;
+ var FrontSide = 0;
+ var BackSide = 1;
+ var DoubleSide = 2;
+ var FlatShading = 1;
+ var NoColors = 0;
+ var FaceColors = 1;
+ var VertexColors = 2;
+ var NoBlending = 0;
+ var NormalBlending = 1;
+ var AdditiveBlending = 2;
+ var SubtractiveBlending = 3;
+ var MultiplyBlending = 4;
+ var CustomBlending = 5;
+ var AddEquation = 100;
+ var SubtractEquation = 101;
+ var ReverseSubtractEquation = 102;
+ var MinEquation = 103;
+ var MaxEquation = 104;
+ var ZeroFactor = 200;
+ var OneFactor = 201;
+ var SrcColorFactor = 202;
+ var OneMinusSrcColorFactor = 203;
+ var SrcAlphaFactor = 204;
+ var OneMinusSrcAlphaFactor = 205;
+ var DstAlphaFactor = 206;
+ var OneMinusDstAlphaFactor = 207;
+ var DstColorFactor = 208;
+ var OneMinusDstColorFactor = 209;
+ var SrcAlphaSaturateFactor = 210;
+ var NeverDepth = 0;
+ var AlwaysDepth = 1;
+ var LessDepth = 2;
+ var LessEqualDepth = 3;
+ var EqualDepth = 4;
+ var GreaterEqualDepth = 5;
+ var GreaterDepth = 6;
+ var NotEqualDepth = 7;
+ var MultiplyOperation = 0;
+ var MixOperation = 1;
+ var AddOperation = 2;
+ var NoToneMapping = 0;
+ var LinearToneMapping = 1;
+ var ReinhardToneMapping = 2;
+ var Uncharted2ToneMapping = 3;
+ var CineonToneMapping = 4;
+ var UVMapping = 300;
+ var CubeReflectionMapping = 301;
+ var CubeRefractionMapping = 302;
+ var EquirectangularReflectionMapping = 303;
+ var EquirectangularRefractionMapping = 304;
+ var SphericalReflectionMapping = 305;
+ var CubeUVReflectionMapping = 306;
+ var CubeUVRefractionMapping = 307;
+ var RepeatWrapping = 1000;
+ var ClampToEdgeWrapping = 1001;
+ var MirroredRepeatWrapping = 1002;
+ var NearestFilter = 1003;
+ var NearestMipMapNearestFilter = 1004;
+ var NearestMipMapLinearFilter = 1005;
+ var LinearFilter = 1006;
+ var LinearMipMapNearestFilter = 1007;
+ var LinearMipMapLinearFilter = 1008;
+ var UnsignedByteType = 1009;
+ var ByteType = 1010;
+ var ShortType = 1011;
+ var UnsignedShortType = 1012;
+ var IntType = 1013;
+ var UnsignedIntType = 1014;
+ var FloatType = 1015;
+ var HalfFloatType = 1016;
+ var UnsignedShort4444Type = 1017;
+ var UnsignedShort5551Type = 1018;
+ var UnsignedShort565Type = 1019;
+ var UnsignedInt248Type = 1020;
+ var AlphaFormat = 1021;
+ var RGBFormat = 1022;
+ var RGBAFormat = 1023;
+ var LuminanceFormat = 1024;
+ var LuminanceAlphaFormat = 1025;
+ var DepthFormat = 1026;
+ var DepthStencilFormat = 1027;
+ var RGB_S3TC_DXT1_Format = 33776;
+ var RGBA_S3TC_DXT1_Format = 33777;
+ var RGBA_S3TC_DXT3_Format = 33778;
+ var RGBA_S3TC_DXT5_Format = 33779;
+ var RGB_PVRTC_4BPPV1_Format = 35840;
+ var RGB_PVRTC_2BPPV1_Format = 35841;
+ var RGBA_PVRTC_4BPPV1_Format = 35842;
+ var RGBA_PVRTC_2BPPV1_Format = 35843;
+ var RGB_ETC1_Format = 36196;
+ var RGBA_ASTC_4x4_Format = 37808;
+ var RGBA_ASTC_5x4_Format = 37809;
+ var RGBA_ASTC_5x5_Format = 37810;
+ var RGBA_ASTC_6x5_Format = 37811;
+ var RGBA_ASTC_6x6_Format = 37812;
+ var RGBA_ASTC_8x5_Format = 37813;
+ var RGBA_ASTC_8x6_Format = 37814;
+ var RGBA_ASTC_8x8_Format = 37815;
+ var RGBA_ASTC_10x5_Format = 37816;
+ var RGBA_ASTC_10x6_Format = 37817;
+ var RGBA_ASTC_10x8_Format = 37818;
+ var RGBA_ASTC_10x10_Format = 37819;
+ var RGBA_ASTC_12x10_Format = 37820;
+ var RGBA_ASTC_12x12_Format = 37821;
+ var LoopOnce = 2200;
+ var LoopRepeat = 2201;
+ var LoopPingPong = 2202;
+ var InterpolateDiscrete = 2300;
+ var InterpolateLinear = 2301;
+ var InterpolateSmooth = 2302;
+ var ZeroCurvatureEnding = 2400;
+ var ZeroSlopeEnding = 2401;
+ var WrapAroundEnding = 2402;
+ var TrianglesDrawMode = 0;
+ var TriangleStripDrawMode = 1;
+ var TriangleFanDrawMode = 2;
+ var LinearEncoding = 3000;
+ var sRGBEncoding = 3001;
+ var GammaEncoding = 3007;
+ var RGBEEncoding = 3002;
+ var RGBM7Encoding = 3004;
+ var RGBM16Encoding = 3005;
+ var RGBDEncoding = 3006;
+ var BasicDepthPacking = 3200;
+ var RGBADepthPacking = 3201;
+
+ /**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+ var _Math = {
+
+ DEG2RAD: Math.PI / 180,
+ RAD2DEG: 180 / Math.PI,
+
+ generateUUID: ( function () {
+
+ // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
+
+ var lut = [];
+
+ for ( var i = 0; i < 256; i ++ ) {
+
+ lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 );
+
+ }
+
+ return function generateUUID() {
+
+ var d0 = Math.random() * 0xffffffff | 0;
+ var d1 = Math.random() * 0xffffffff | 0;
+ var d2 = Math.random() * 0xffffffff | 0;
+ var d3 = Math.random() * 0xffffffff | 0;
+ var uuid = lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + '-' +
+ lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + '-' + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + '-' +
+ lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + '-' + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] +
+ lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ];
+
+ // .toUpperCase() here flattens concatenated strings to save heap memory space.
+ return uuid.toUpperCase();
+
+ };
+
+ } )(),
+
+ clamp: function ( value, min, max ) {
+
+ return Math.max( min, Math.min( max, value ) );
+
+ },
+
+ // compute euclidian modulo of m % n
+ // https://en.wikipedia.org/wiki/Modulo_operation
+
+ euclideanModulo: function ( n, m ) {
+
+ return ( ( n % m ) + m ) % m;
+
+ },
+
+ // Linear mapping from range to range
+
+ mapLinear: function ( x, a1, a2, b1, b2 ) {
+
+ return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
+
+ },
+
+ // https://en.wikipedia.org/wiki/Linear_interpolation
+
+ lerp: function ( x, y, t ) {
+
+ return ( 1 - t ) * x + t * y;
+
+ },
+
+ // http://en.wikipedia.org/wiki/Smoothstep
+
+ smoothstep: function ( x, min, max ) {
+
+ if ( x <= min ) return 0;
+ if ( x >= max ) return 1;
+
+ x = ( x - min ) / ( max - min );
+
+ return x * x * ( 3 - 2 * x );
+
+ },
+
+ smootherstep: function ( x, min, max ) {
+
+ if ( x <= min ) return 0;
+ if ( x >= max ) return 1;
+
+ x = ( x - min ) / ( max - min );
+
+ return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
+
+ },
+
+ // Random integer from interval
+
+ randInt: function ( low, high ) {
+
+ return low + Math.floor( Math.random() * ( high - low + 1 ) );
+
+ },
+
+ // Random float from interval
+
+ randFloat: function ( low, high ) {
+
+ return low + Math.random() * ( high - low );
+
+ },
+
+ // Random float from <-range/2, range/2> interval
+
+ randFloatSpread: function ( range ) {
+
+ return range * ( 0.5 - Math.random() );
+
+ },
+
+ degToRad: function ( degrees ) {
+
+ return degrees * _Math.DEG2RAD;
+
+ },
+
+ radToDeg: function ( radians ) {
+
+ return radians * _Math.RAD2DEG;
+
+ },
+
+ isPowerOfTwo: function ( value ) {
+
+ return ( value & ( value - 1 ) ) === 0 && value !== 0;
+
+ },
+
+ ceilPowerOfTwo: function ( value ) {
+
+ return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );
+
+ },
+
+ floorPowerOfTwo: function ( value ) {
+
+ return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );
+
+ }
+
+ };
+
+ /**
+ * @author mrdoob / http://mrdoob.com/
+ * @author philogb / http://blog.thejit.org/
+ * @author egraether / http://egraether.com/
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ */
+
+ function Vector2( x, y ) {
+
+ this.x = x || 0;
+ this.y = y || 0;
+
+ }
+
+ Object.defineProperties( Vector2.prototype, {
+
+ "width": {
+
+ get: function () {
+
+ return this.x;
+
+ },
+
+ set: function ( value ) {
+
+ this.x = value;
+
+ }
+
+ },
+
+ "height": {
+
+ get: function () {
+
+ return this.y;
+
+ },
+
+ set: function ( value ) {
+
+ this.y = value;
+
+ }
+
+ }
+
+ } );
+
+ Object.assign( Vector2.prototype, {
+
+ isVector2: true,
+
+ set: function ( x, y ) {
+
+ this.x = x;
+ this.y = y;
+
+ return this;
+
+ },
+
+ setScalar: function ( scalar ) {
+
+ this.x = scalar;
+ this.y = scalar;
+
+ return this;
+
+ },
+
+ setX: function ( x ) {
+
+ this.x = x;
+
+ return this;
+
+ },
+
+ setY: function ( y ) {
+
+ this.y = y;
+
+ return this;
+
+ },
+
+ setComponent: function ( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ return this;
+
+ },
+
+ getComponent: function ( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this.x, this.y );
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+
+ return this;
+
+ },
+
+ addScaledVector: function ( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+
+ return this;
+
+ },
+
+ subScalar: function ( s ) {
+
+ this.x -= s;
+ this.y -= s;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+
+ return this;
+
+ },
+
+ multiply: function ( v ) {
+
+ this.x *= v.x;
+ this.y *= v.y;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( scalar ) {
+
+ this.x *= scalar;
+ this.y *= scalar;
+
+ return this;
+
+ },
+
+ divide: function ( v ) {
+
+ this.x /= v.x;
+ this.y /= v.y;
+
+ return this;
+
+ },
+
+ divideScalar: function ( scalar ) {
+
+ return this.multiplyScalar( 1 / scalar );
+
+ },
+
+ applyMatrix3: function ( m ) {
+
+ var x = this.x, y = this.y;
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ];
+ this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ];
+
+ return this;
+
+ },
+
+ min: function ( v ) {
+
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // assumes min < max, componentwise
+
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+
+ return this;
+
+ },
+
+ clampScalar: function () {
+
+ var min = new Vector2();
+ var max = new Vector2();
+
+ return function clampScalar( minVal, maxVal ) {
+
+ min.set( minVal, minVal );
+ max.set( maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ }(),
+
+ clampLength: function ( min, max ) {
+
+ var length = this.length();
+
+ return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
+
+ },
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y;
+
+ },
+
+ lengthSq: function () {
+
+ return this.x * this.x + this.y * this.y;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this.x * this.x + this.y * this.y );
+
+ },
+
+ manhattanLength: function () {
+
+ return Math.abs( this.x ) + Math.abs( this.y );
+
+ },
+
+ normalize: function () {
+
+ return this.divideScalar( this.length() || 1 );
+
+ },
+
+ angle: function () {
+
+ // computes the angle in radians with respect to the positive x-axis
+
+ var angle = Math.atan2( this.y, this.x );
+
+ if ( angle < 0 ) angle += 2 * Math.PI;
+
+ return angle;
+
+ },
+
+ distanceTo: function ( v ) {
+
+ return Math.sqrt( this.distanceToSquared( v ) );
+
+ },
+
+ distanceToSquared: function ( v ) {
+
+ var dx = this.x - v.x, dy = this.y - v.y;
+ return dx * dx + dy * dy;
+
+ },
+
+ manhattanDistanceTo: function ( v ) {
+
+ return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y );
+
+ },
+
+ setLength: function ( length ) {
+
+ return this.normalize().multiplyScalar( length );
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+
+ return this;
+
+ },
+
+ lerpVectors: function ( v1, v2, alpha ) {
+
+ return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+
+ return array;
+
+ },
+
+ fromBufferAttribute: function ( attribute, index, offset ) {
+
+ if ( offset !== undefined ) {
+
+ console.warn( 'THREE.Vector2: offset has been removed from .fromBufferAttribute().' );
+
+ }
+
+ this.x = attribute.getX( index );
+ this.y = attribute.getY( index );
+
+ return this;
+
+ },
+
+ rotateAround: function ( center, angle ) {
+
+ var c = Math.cos( angle ), s = Math.sin( angle );
+
+ var x = this.x - center.x;
+ var y = this.y - center.y;
+
+ this.x = x * c - y * s + center.x;
+ this.y = x * s + y * c + center.y;
+
+ return this;
+
+ }
+
+ } );
+
+ /**
+ * @author mrdoob / http://mrdoob.com/
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author philogb / http://blog.thejit.org/
+ * @author jordi_ros / http://plattsoft.com
+ * @author D1plo1d / http://github.com/D1plo1d
+ * @author alteredq / http://alteredqualia.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author timknip / http://www.floorplanner.com/
+ * @author bhouston / http://clara.io
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+ function Matrix4() {
+
+ this.elements = [
+
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+
+ ];
+
+ if ( arguments.length > 0 ) {
+
+ console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );
+
+ }
+
+ }
+
+ Object.assign( Matrix4.prototype, {
+
+ isMatrix4: true,
+
+ set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
+
+ var te = this.elements;
+
+ te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
+ te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
+ te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
+ te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;
+
+ return this;
+
+ },
+
+ identity: function () {
+
+ this.set(
+
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new Matrix4().fromArray( this.elements );
+
+ },
+
+ copy: function ( m ) {
+
+ var te = this.elements;
+ var me = m.elements;
+
+ te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ];
+ te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ];
+ te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ];
+ te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ];
+
+ return this;
+
+ },
+
+ copyPosition: function ( m ) {
+
+ var te = this.elements, me = m.elements;
+
+ te[ 12 ] = me[ 12 ];
+ te[ 13 ] = me[ 13 ];
+ te[ 14 ] = me[ 14 ];
+
+ return this;
+
+ },
+
+ extractBasis: function ( xAxis, yAxis, zAxis ) {
+
+ xAxis.setFromMatrixColumn( this, 0 );
+ yAxis.setFromMatrixColumn( this, 1 );
+ zAxis.setFromMatrixColumn( this, 2 );
+
+ return this;
+
+ },
+
+ makeBasis: function ( xAxis, yAxis, zAxis ) {
+
+ this.set(
+ xAxis.x, yAxis.x, zAxis.x, 0,
+ xAxis.y, yAxis.y, zAxis.y, 0,
+ xAxis.z, yAxis.z, zAxis.z, 0,
+ 0, 0, 0, 1
+ );
+
+ return this;
+
+ },
+
+ extractRotation: function () {
+
+ var v1 = new Vector3();
+
+ return function extractRotation( m ) {
+
+ // this method does not support reflection matrices
+
+ var te = this.elements;
+ var me = m.elements;
+
+ var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length();
+ var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length();
+ var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length();
+
+ te[ 0 ] = me[ 0 ] * scaleX;
+ te[ 1 ] = me[ 1 ] * scaleX;
+ te[ 2 ] = me[ 2 ] * scaleX;
+ te[ 3 ] = 0;
+
+ te[ 4 ] = me[ 4 ] * scaleY;
+ te[ 5 ] = me[ 5 ] * scaleY;
+ te[ 6 ] = me[ 6 ] * scaleY;
+ te[ 7 ] = 0;
+
+ te[ 8 ] = me[ 8 ] * scaleZ;
+ te[ 9 ] = me[ 9 ] * scaleZ;
+ te[ 10 ] = me[ 10 ] * scaleZ;
+ te[ 11 ] = 0;
+
+ te[ 12 ] = 0;
+ te[ 13 ] = 0;
+ te[ 14 ] = 0;
+ te[ 15 ] = 1;
+
+ return this;
+
+ };
+
+ }(),
+
+ makeRotationFromEuler: function ( euler ) {
+
+ if ( ! ( euler && euler.isEuler ) ) {
+
+ console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ var te = this.elements;
+
+ var x = euler.x, y = euler.y, z = euler.z;
+ var a = Math.cos( x ), b = Math.sin( x );
+ var c = Math.cos( y ), d = Math.sin( y );
+ var e = Math.cos( z ), f = Math.sin( z );
+
+ if ( euler.order === 'XYZ' ) {
+
+ var ae = a * e, af = a * f, be = b * e, bf = b * f;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = - c * f;
+ te[ 8 ] = d;
+
+ te[ 1 ] = af + be * d;
+ te[ 5 ] = ae - bf * d;
+ te[ 9 ] = - b * c;
+
+ te[ 2 ] = bf - ae * d;
+ te[ 6 ] = be + af * d;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'YXZ' ) {
+
+ var ce = c * e, cf = c * f, de = d * e, df = d * f;
+
+ te[ 0 ] = ce + df * b;
+ te[ 4 ] = de * b - cf;
+ te[ 8 ] = a * d;
+
+ te[ 1 ] = a * f;
+ te[ 5 ] = a * e;
+ te[ 9 ] = - b;
+
+ te[ 2 ] = cf * b - de;
+ te[ 6 ] = df + ce * b;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'ZXY' ) {
+
+ var ce = c * e, cf = c * f, de = d * e, df = d * f;
+
+ te[ 0 ] = ce - df * b;
+ te[ 4 ] = - a * f;
+ te[ 8 ] = de + cf * b;
+
+ te[ 1 ] = cf + de * b;
+ te[ 5 ] = a * e;
+ te[ 9 ] = df - ce * b;
+
+ te[ 2 ] = - a * d;
+ te[ 6 ] = b;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'ZYX' ) {
+
+ var ae = a * e, af = a * f, be = b * e, bf = b * f;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = be * d - af;
+ te[ 8 ] = ae * d + bf;
+
+ te[ 1 ] = c * f;
+ te[ 5 ] = bf * d + ae;
+ te[ 9 ] = af * d - be;
+
+ te[ 2 ] = - d;
+ te[ 6 ] = b * c;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'YZX' ) {
+
+ var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = bd - ac * f;
+ te[ 8 ] = bc * f + ad;
+
+ te[ 1 ] = f;
+ te[ 5 ] = a * e;
+ te[ 9 ] = - b * e;
+
+ te[ 2 ] = - d * e;
+ te[ 6 ] = ad * f + bc;
+ te[ 10 ] = ac - bd * f;
+
+ } else if ( euler.order === 'XZY' ) {
+
+ var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = - f;
+ te[ 8 ] = d * e;
+
+ te[ 1 ] = ac * f + bd;
+ te[ 5 ] = a * e;
+ te[ 9 ] = ad * f - bc;
+
+ te[ 2 ] = bc * f - ad;
+ te[ 6 ] = b * e;
+ te[ 10 ] = bd * f + ac;
+
+ }
+
+ // bottom row
+ te[ 3 ] = 0;
+ te[ 7 ] = 0;
+ te[ 11 ] = 0;
+
+ // last column
+ te[ 12 ] = 0;
+ te[ 13 ] = 0;
+ te[ 14 ] = 0;
+ te[ 15 ] = 1;
+
+ return this;
+
+ },
+
+ makeRotationFromQuaternion: function () {
+
+ var zero = new Vector3( 0, 0, 0 );
+ var one = new Vector3( 1, 1, 1 );
+
+ return function makeRotationFromQuaternion( q ) {
+
+ return this.compose( zero, q, one );
+
+ };
+
+ }(),
+
+ lookAt: function () {
+
+ var x = new Vector3();
+ var y = new Vector3();
+ var z = new Vector3();
+
+ return function lookAt( eye, target, up ) {
+
+ var te = this.elements;
+
+ z.subVectors( eye, target );
+
+ if ( z.lengthSq() === 0 ) {
+
+ // eye and target are in the same position
+
+ z.z = 1;
+
+ }
+
+ z.normalize();
+ x.crossVectors( up, z );
+
+ if ( x.lengthSq() === 0 ) {
+
+ // up and z are parallel
+
+ if ( Math.abs( up.z ) === 1 ) {
+
+ z.x += 0.0001;
+
+ } else {
+
+ z.z += 0.0001;
+
+ }
+
+ z.normalize();
+ x.crossVectors( up, z );
+
+ }
+
+ x.normalize();
+ y.crossVectors( z, x );
+
+ te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;
+ te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;
+ te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;
+
+ return this;
+
+ };
+
+ }(),
+
+ multiply: function ( m, n ) {
+
+ if ( n !== undefined ) {
+
+ console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
+ return this.multiplyMatrices( m, n );
+
+ }
+
+ return this.multiplyMatrices( this, m );
+
+ },
+
+ premultiply: function ( m ) {
+
+ return this.multiplyMatrices( m, this );
+
+ },
+
+ multiplyMatrices: function ( a, b ) {
+
+ var ae = a.elements;
+ var be = b.elements;
+ var te = this.elements;
+
+ var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
+ var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
+ var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
+ var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];
+
+ var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
+ var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
+ var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
+ var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];
+
+ te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
+ te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
+ te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
+ te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
+
+ te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
+ te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
+ te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
+ te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
+
+ te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
+ te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
+ te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
+ te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
+
+ te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
+ te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
+ te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
+ te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( s ) {
+
+ var te = this.elements;
+
+ te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
+ te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
+ te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
+ te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;
+
+ return this;
+
+ },
+
+ applyToBufferAttribute: function () {
+
+ var v1 = new Vector3();
+
+ return function applyToBufferAttribute( attribute ) {
+
+ for ( var i = 0, l = attribute.count; i < l; i ++ ) {
+
+ v1.x = attribute.getX( i );
+ v1.y = attribute.getY( i );
+ v1.z = attribute.getZ( i );
+
+ v1.applyMatrix4( this );
+
+ attribute.setXYZ( i, v1.x, v1.y, v1.z );
+
+ }
+
+ return attribute;
+
+ };
+
+ }(),
+
+ determinant: function () {
+
+ var te = this.elements;
+
+ var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
+ var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
+ var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
+ var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];
+
+ //TODO: make this more efficient
+ //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )
+
+ return (
+ n41 * (
+ + n14 * n23 * n32
+ - n13 * n24 * n32
+ - n14 * n22 * n33
+ + n12 * n24 * n33
+ + n13 * n22 * n34
+ - n12 * n23 * n34
+ ) +
+ n42 * (
+ + n11 * n23 * n34
+ - n11 * n24 * n33
+ + n14 * n21 * n33
+ - n13 * n21 * n34
+ + n13 * n24 * n31
+ - n14 * n23 * n31
+ ) +
+ n43 * (
+ + n11 * n24 * n32
+ - n11 * n22 * n34
+ - n14 * n21 * n32
+ + n12 * n21 * n34
+ + n14 * n22 * n31
+ - n12 * n24 * n31
+ ) +
+ n44 * (
+ - n13 * n22 * n31
+ - n11 * n23 * n32
+ + n11 * n22 * n33
+ + n13 * n21 * n32
+ - n12 * n21 * n33
+ + n12 * n23 * n31
+ )
+
+ );
+
+ },
+
+ transpose: function () {
+
+ var te = this.elements;
+ var tmp;
+
+ tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
+ tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
+ tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;
+
+ tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
+ tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
+ tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;
+
+ return this;
+
+ },
+
+ setPosition: function ( v ) {
+
+ var te = this.elements;
+
+ te[ 12 ] = v.x;
+ te[ 13 ] = v.y;
+ te[ 14 ] = v.z;
+
+ return this;
+
+ },
+
+ getInverse: function ( m, throwOnDegenerate ) {
+
+ // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
+ var te = this.elements,
+ me = m.elements,
+
+ n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ],
+ n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ],
+ n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ],
+ n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ],
+
+ t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,
+ t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,
+ t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,
+ t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
+
+ var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
+
+ if ( det === 0 ) {
+
+ var msg = "THREE.Matrix4: .getInverse() can't invert matrix, determinant is 0";
+
+ if ( throwOnDegenerate === true ) {
+
+ throw new Error( msg );
+
+ } else {
+
+ console.warn( msg );
+
+ }
+
+ return this.identity();
+
+ }
+
+ var detInv = 1 / det;
+
+ te[ 0 ] = t11 * detInv;
+ te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;
+ te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;
+ te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;
+
+ te[ 4 ] = t12 * detInv;
+ te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;
+ te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;
+ te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;
+
+ te[ 8 ] = t13 * detInv;
+ te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;
+ te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;
+ te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;
+
+ te[ 12 ] = t14 * detInv;
+ te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;
+ te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;
+ te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;
+
+ return this;
+
+ },
+
+ scale: function ( v ) {
+
+ var te = this.elements;
+ var x = v.x, y = v.y, z = v.z;
+
+ te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;
+ te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;
+ te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;
+ te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;
+
+ return this;
+
+ },
+
+ getMaxScaleOnAxis: function () {
+
+ var te = this.elements;
+
+ var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];
+ var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];
+ var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];
+
+ return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );
+
+ },
+
+ makeTranslation: function ( x, y, z ) {
+
+ this.set(
+
+ 1, 0, 0, x,
+ 0, 1, 0, y,
+ 0, 0, 1, z,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationX: function ( theta ) {
+
+ var c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ 1, 0, 0, 0,
+ 0, c, - s, 0,
+ 0, s, c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationY: function ( theta ) {
+
+ var c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ c, 0, s, 0,
+ 0, 1, 0, 0,
+ - s, 0, c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationZ: function ( theta ) {
+
+ var c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ c, - s, 0, 0,
+ s, c, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationAxis: function ( axis, angle ) {
+
+ // Based on http://www.gamedev.net/reference/articles/article1199.asp
+
+ var c = Math.cos( angle );
+ var s = Math.sin( angle );
+ var t = 1 - c;
+ var x = axis.x, y = axis.y, z = axis.z;
+ var tx = t * x, ty = t * y;
+
+ this.set(
+
+ tx * x + c, tx * y - s * z, tx * z + s * y, 0,
+ tx * y + s * z, ty * y + c, ty * z - s * x, 0,
+ tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeScale: function ( x, y, z ) {
+
+ this.set(
+
+ x, 0, 0, 0,
+ 0, y, 0, 0,
+ 0, 0, z, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeShear: function ( x, y, z ) {
+
+ this.set(
+
+ 1, y, z, 0,
+ x, 1, z, 0,
+ x, y, 1, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ compose: function ( position, quaternion, scale ) {
+
+ var te = this.elements;
+
+ var x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w;
+ var x2 = x + x, y2 = y + y, z2 = z + z;
+ var xx = x * x2, xy = x * y2, xz = x * z2;
+ var yy = y * y2, yz = y * z2, zz = z * z2;
+ var wx = w * x2, wy = w * y2, wz = w * z2;
+
+ var sx = scale.x, sy = scale.y, sz = scale.z;
+
+ te[ 0 ] = ( 1 - ( yy + zz ) ) * sx;
+ te[ 1 ] = ( xy + wz ) * sx;
+ te[ 2 ] = ( xz - wy ) * sx;
+ te[ 3 ] = 0;
+
+ te[ 4 ] = ( xy - wz ) * sy;
+ te[ 5 ] = ( 1 - ( xx + zz ) ) * sy;
+ te[ 6 ] = ( yz + wx ) * sy;
+ te[ 7 ] = 0;
+
+ te[ 8 ] = ( xz + wy ) * sz;
+ te[ 9 ] = ( yz - wx ) * sz;
+ te[ 10 ] = ( 1 - ( xx + yy ) ) * sz;
+ te[ 11 ] = 0;
+
+ te[ 12 ] = position.x;
+ te[ 13 ] = position.y;
+ te[ 14 ] = position.z;
+ te[ 15 ] = 1;
+
+ return this;
+
+ },
+
+ decompose: function () {
+
+ var vector = new Vector3();
+ var matrix = new Matrix4();
+
+ return function decompose( position, quaternion, scale ) {
+
+ var te = this.elements;
+
+ var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
+ var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
+ var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();
+
+ // if determine is negative, we need to invert one scale
+ var det = this.determinant();
+ if ( det < 0 ) sx = - sx;
+
+ position.x = te[ 12 ];
+ position.y = te[ 13 ];
+ position.z = te[ 14 ];
+
+ // scale the rotation part
+ matrix.copy( this );
+
+ var invSX = 1 / sx;
+ var invSY = 1 / sy;
+ var invSZ = 1 / sz;
+
+ matrix.elements[ 0 ] *= invSX;
+ matrix.elements[ 1 ] *= invSX;
+ matrix.elements[ 2 ] *= invSX;
+
+ matrix.elements[ 4 ] *= invSY;
+ matrix.elements[ 5 ] *= invSY;
+ matrix.elements[ 6 ] *= invSY;
+
+ matrix.elements[ 8 ] *= invSZ;
+ matrix.elements[ 9 ] *= invSZ;
+ matrix.elements[ 10 ] *= invSZ;
+
+ quaternion.setFromRotationMatrix( matrix );
+
+ scale.x = sx;
+ scale.y = sy;
+ scale.z = sz;
+
+ return this;
+
+ };
+
+ }(),
+
+ makePerspective: function ( left, right, top, bottom, near, far ) {
+
+ if ( far === undefined ) {
+
+ console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' );
+
+ }
+
+ var te = this.elements;
+ var x = 2 * near / ( right - left );
+ var y = 2 * near / ( top - bottom );
+
+ var a = ( right + left ) / ( right - left );
+ var b = ( top + bottom ) / ( top - bottom );
+ var c = - ( far + near ) / ( far - near );
+ var d = - 2 * far * near / ( far - near );
+
+ te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0;
+ te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0;
+ te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d;
+ te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0;
+
+ return this;
+
+ },
+
+ makeOrthographic: function ( left, right, top, bottom, near, far ) {
+
+ var te = this.elements;
+ var w = 1.0 / ( right - left );
+ var h = 1.0 / ( top - bottom );
+ var p = 1.0 / ( far - near );
+
+ var x = ( right + left ) * w;
+ var y = ( top + bottom ) * h;
+ var z = ( far + near ) * p;
+
+ te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x;
+ te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y;
+ te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z;
+ te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1;
+
+ return this;
+
+ },
+
+ equals: function ( matrix ) {
+
+ var te = this.elements;
+ var me = matrix.elements;
+
+ for ( var i = 0; i < 16; i ++ ) {
+
+ if ( te[ i ] !== me[ i ] ) return false;
+
+ }
+
+ return true;
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ for ( var i = 0; i < 16; i ++ ) {
+
+ this.elements[ i ] = array[ i + offset ];
+
+ }
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ var te = this.elements;
+
+ array[ offset ] = te[ 0 ];
+ array[ offset + 1 ] = te[ 1 ];
+ array[ offset + 2 ] = te[ 2 ];
+ array[ offset + 3 ] = te[ 3 ];
+
+ array[ offset + 4 ] = te[ 4 ];
+ array[ offset + 5 ] = te[ 5 ];
+ array[ offset + 6 ] = te[ 6 ];
+ array[ offset + 7 ] = te[ 7 ];
+
+ array[ offset + 8 ] = te[ 8 ];
+ array[ offset + 9 ] = te[ 9 ];
+ array[ offset + 10 ] = te[ 10 ];
+ array[ offset + 11 ] = te[ 11 ];
+
+ array[ offset + 12 ] = te[ 12 ];
+ array[ offset + 13 ] = te[ 13 ];
+ array[ offset + 14 ] = te[ 14 ];
+ array[ offset + 15 ] = te[ 15 ];
+
+ return array;
+
+ }
+
+ } );
+
+ /**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://clara.io
+ */
+
+ function Quaternion( x, y, z, w ) {
+
+ this._x = x || 0;
+ this._y = y || 0;
+ this._z = z || 0;
+ this._w = ( w !== undefined ) ? w : 1;
+
+ }
+
+ Object.assign( Quaternion, {
+
+ slerp: function ( qa, qb, qm, t ) {
+
+ return qm.copy( qa ).slerp( qb, t );
+
+ },
+
+ slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {
+
+ // fuzz-free, array-based Quaternion SLERP operation
+
+ var x0 = src0[ srcOffset0 + 0 ],
+ y0 = src0[ srcOffset0 + 1 ],
+ z0 = src0[ srcOffset0 + 2 ],
+ w0 = src0[ srcOffset0 + 3 ],
+
+ x1 = src1[ srcOffset1 + 0 ],
+ y1 = src1[ srcOffset1 + 1 ],
+ z1 = src1[ srcOffset1 + 2 ],
+ w1 = src1[ srcOffset1 + 3 ];
+
+ if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {
+
+ var s = 1 - t,
+
+ cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,
+
+ dir = ( cos >= 0 ? 1 : - 1 ),
+ sqrSin = 1 - cos * cos;
+
+ // Skip the Slerp for tiny steps to avoid numeric problems:
+ if ( sqrSin > Number.EPSILON ) {
+
+ var sin = Math.sqrt( sqrSin ),
+ len = Math.atan2( sin, cos * dir );
+
+ s = Math.sin( s * len ) / sin;
+ t = Math.sin( t * len ) / sin;
+
+ }
+
+ var tDir = t * dir;
+
+ x0 = x0 * s + x1 * tDir;
+ y0 = y0 * s + y1 * tDir;
+ z0 = z0 * s + z1 * tDir;
+ w0 = w0 * s + w1 * tDir;
+
+ // Normalize in case we just did a lerp:
+ if ( s === 1 - t ) {
+
+ var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );
+
+ x0 *= f;
+ y0 *= f;
+ z0 *= f;
+ w0 *= f;
+
+ }
+
+ }
+
+ dst[ dstOffset ] = x0;
+ dst[ dstOffset + 1 ] = y0;
+ dst[ dstOffset + 2 ] = z0;
+ dst[ dstOffset + 3 ] = w0;
+
+ }
+
+ } );
+
+ Object.defineProperties( Quaternion.prototype, {
+
+ x: {
+
+ get: function () {
+
+ return this._x;
+
+ },
+
+ set: function ( value ) {
+
+ this._x = value;
+ this.onChangeCallback();
+
+ }
+
+ },
+
+ y: {
+
+ get: function () {
+
+ return this._y;
+
+ },
+
+ set: function ( value ) {
+
+ this._y = value;
+ this.onChangeCallback();
+
+ }
+
+ },
+
+ z: {
+
+ get: function () {
+
+ return this._z;
+
+ },
+
+ set: function ( value ) {
+
+ this._z = value;
+ this.onChangeCallback();
+
+ }
+
+ },
+
+ w: {
+
+ get: function () {
+
+ return this._w;
+
+ },
+
+ set: function ( value ) {
+
+ this._w = value;
+ this.onChangeCallback();
+
+ }
+
+ }
+
+ } );
+
+ Object.assign( Quaternion.prototype, {
+
+ set: function ( x, y, z, w ) {
+
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._w = w;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this._x, this._y, this._z, this._w );
+
+ },
+
+ copy: function ( quaternion ) {
+
+ this._x = quaternion.x;
+ this._y = quaternion.y;
+ this._z = quaternion.z;
+ this._w = quaternion.w;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromEuler: function ( euler, update ) {
+
+ if ( ! ( euler && euler.isEuler ) ) {
+
+ throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ var x = euler._x, y = euler._y, z = euler._z, order = euler.order;
+
+ // http://www.mathworks.com/matlabcentral/fileexchange/
+ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
+ // content/SpinCalc.m
+
+ var cos = Math.cos;
+ var sin = Math.sin;
+
+ var c1 = cos( x / 2 );
+ var c2 = cos( y / 2 );
+ var c3 = cos( z / 2 );
+
+ var s1 = sin( x / 2 );
+ var s2 = sin( y / 2 );
+ var s3 = sin( z / 2 );
+
+ if ( order === 'XYZ' ) {
+
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if ( order === 'YXZ' ) {
+
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+
+ } else if ( order === 'ZXY' ) {
+
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if ( order === 'ZYX' ) {
+
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+
+ } else if ( order === 'YZX' ) {
+
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if ( order === 'XZY' ) {
+
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+
+ }
+
+ if ( update !== false ) this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromAxisAngle: function ( axis, angle ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
+
+ // assumes axis is normalized
+
+ var halfAngle = angle / 2, s = Math.sin( halfAngle );
+
+ this._x = axis.x * s;
+ this._y = axis.y * s;
+ this._z = axis.z * s;
+ this._w = Math.cos( halfAngle );
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromRotationMatrix: function ( m ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ var te = m.elements,
+
+ m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+ m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+ m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
+
+ trace = m11 + m22 + m33,
+ s;
+
+ if ( trace > 0 ) {
+
+ s = 0.5 / Math.sqrt( trace + 1.0 );
+
+ this._w = 0.25 / s;
+ this._x = ( m32 - m23 ) * s;
+ this._y = ( m13 - m31 ) * s;
+ this._z = ( m21 - m12 ) * s;
+
+ } else if ( m11 > m22 && m11 > m33 ) {
+
+ s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
+
+ this._w = ( m32 - m23 ) / s;
+ this._x = 0.25 * s;
+ this._y = ( m12 + m21 ) / s;
+ this._z = ( m13 + m31 ) / s;
+
+ } else if ( m22 > m33 ) {
+
+ s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
+
+ this._w = ( m13 - m31 ) / s;
+ this._x = ( m12 + m21 ) / s;
+ this._y = 0.25 * s;
+ this._z = ( m23 + m32 ) / s;
+
+ } else {
+
+ s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
+
+ this._w = ( m21 - m12 ) / s;
+ this._x = ( m13 + m31 ) / s;
+ this._y = ( m23 + m32 ) / s;
+ this._z = 0.25 * s;
+
+ }
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromUnitVectors: function () {
+
+ // assumes direction vectors vFrom and vTo are normalized
+
+ var v1 = new Vector3();
+ var r;
+
+ var EPS = 0.000001;
+
+ return function setFromUnitVectors( vFrom, vTo ) {
+
+ if ( v1 === undefined ) v1 = new Vector3();
+
+ r = vFrom.dot( vTo ) + 1;
+
+ if ( r < EPS ) {
+
+ r = 0;
+
+ if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
+
+ v1.set( - vFrom.y, vFrom.x, 0 );
+
+ } else {
+
+ v1.set( 0, - vFrom.z, vFrom.y );
+
+ }
+
+ } else {
+
+ v1.crossVectors( vFrom, vTo );
+
+ }
+
+ this._x = v1.x;
+ this._y = v1.y;
+ this._z = v1.z;
+ this._w = r;
+
+ return this.normalize();
+
+ };
+
+ }(),
+
+ inverse: function () {
+
+ // quaternion is assumed to have unit length
+
+ return this.conjugate();
+
+ },
+
+ conjugate: function () {
+
+ this._x *= - 1;
+ this._y *= - 1;
+ this._z *= - 1;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
+
+ },
+
+ lengthSq: function () {
+
+ return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
+
+ },
+
+ normalize: function () {
+
+ var l = this.length();
+
+ if ( l === 0 ) {
+
+ this._x = 0;
+ this._y = 0;
+ this._z = 0;
+ this._w = 1;
+
+ } else {
+
+ l = 1 / l;
+
+ this._x = this._x * l;
+ this._y = this._y * l;
+ this._z = this._z * l;
+ this._w = this._w * l;
+
+ }
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ multiply: function ( q, p ) {
+
+ if ( p !== undefined ) {
+
+ console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
+ return this.multiplyQuaternions( q, p );
+
+ }
+
+ return this.multiplyQuaternions( this, q );
+
+ },
+
+ premultiply: function ( q ) {
+
+ return this.multiplyQuaternions( q, this );
+
+ },
+
+ multiplyQuaternions: function ( a, b ) {
+
+ // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
+
+ var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
+ var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
+
+ this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
+ this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
+ this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
+ this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ slerp: function ( qb, t ) {
+
+ if ( t === 0 ) return this;
+ if ( t === 1 ) return this.copy( qb );
+
+ var x = this._x, y = this._y, z = this._z, w = this._w;
+
+ // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
+
+ var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
+
+ if ( cosHalfTheta < 0 ) {
+
+ this._w = - qb._w;
+ this._x = - qb._x;
+ this._y = - qb._y;
+ this._z = - qb._z;
+
+ cosHalfTheta = - cosHalfTheta;
+
+ } else {
+
+ this.copy( qb );
+
+ }
+
+ if ( cosHalfTheta >= 1.0 ) {
+
+ this._w = w;
+ this._x = x;
+ this._y = y;
+ this._z = z;
+
+ return this;
+
+ }
+
+ var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
+
+ if ( Math.abs( sinHalfTheta ) < 0.001 ) {
+
+ this._w = 0.5 * ( w + this._w );
+ this._x = 0.5 * ( x + this._x );
+ this._y = 0.5 * ( y + this._y );
+ this._z = 0.5 * ( z + this._z );
+
+ return this;
+
+ }
+
+ var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );
+ var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
+ ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
+
+ this._w = ( w * ratioA + this._w * ratioB );
+ this._x = ( x * ratioA + this._x * ratioB );
+ this._y = ( y * ratioA + this._y * ratioB );
+ this._z = ( z * ratioA + this._z * ratioB );
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ equals: function ( quaternion ) {
+
+ return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this._x = array[ offset ];
+ this._y = array[ offset + 1 ];
+ this._z = array[ offset + 2 ];
+ this._w = array[ offset + 3 ];
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this._x;
+ array[ offset + 1 ] = this._y;
+ array[ offset + 2 ] = this._z;
+ array[ offset + 3 ] = this._w;
+
+ return array;
+
+ },
+
+ onChange: function ( callback ) {
+
+ this.onChangeCallback = callback;
+
+ return this;
+
+ },
+
+ onChangeCallback: function () {}
+
+ } );
+
+ /**
+ * @author mrdoob / http://mrdoob.com/
+ * @author kile / http://kile.stravaganza.org/
+ * @author philogb / http://blog.thejit.org/
+ * @author mikael emtinger / http://gomo.se/
+ * @author egraether / http://egraether.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+ function Vector3( x, y, z ) {
+
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+
+ }
+
+ Object.assign( Vector3.prototype, {
+
+ isVector3: true,
+
+ set: function ( x, y, z ) {
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+
+ return this;
+
+ },
+
+ setScalar: function ( scalar ) {
+
+ this.x = scalar;
+ this.y = scalar;
+ this.z = scalar;
+
+ return this;
+
+ },
+
+ setX: function ( x ) {
+
+ this.x = x;
+
+ return this;
+
+ },
+
+ setY: function ( y ) {
+
+ this.y = y;
+
+ return this;
+
+ },
+
+ setZ: function ( z ) {
+
+ this.z = z;
+
+ return this;
+
+ },
+
+ setComponent: function ( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ case 2: this.z = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ return this;
+
+ },
+
+ getComponent: function ( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ case 2: return this.z;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this.x, this.y, this.z );
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+ this.z += v.z;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+ this.z += s;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+ this.z = a.z + b.z;
+
+ return this;
+
+ },
+
+ addScaledVector: function ( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+ this.z += v.z * s;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+ this.z -= v.z;
+
+ return this;
+
+ },
+
+ subScalar: function ( s ) {
+
+ this.x -= s;
+ this.y -= s;
+ this.z -= s;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+ this.z = a.z - b.z;
+
+ return this;
+
+ },
+
+ multiply: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
+ return this.multiplyVectors( v, w );
+
+ }
+
+ this.x *= v.x;
+ this.y *= v.y;
+ this.z *= v.z;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( scalar ) {
+
+ this.x *= scalar;
+ this.y *= scalar;
+ this.z *= scalar;
+
+ return this;
+
+ },
+
+ multiplyVectors: function ( a, b ) {
+
+ this.x = a.x * b.x;
+ this.y = a.y * b.y;
+ this.z = a.z * b.z;
+
+ return this;
+
+ },
+
+ applyEuler: function () {
+
+ var quaternion = new Quaternion();
+
+ return function applyEuler( euler ) {
+
+ if ( ! ( euler && euler.isEuler ) ) {
+
+ console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ return this.applyQuaternion( quaternion.setFromEuler( euler ) );
+
+ };
+
+ }(),
+
+ applyAxisAngle: function () {
+
+ var quaternion = new Quaternion();
+
+ return function applyAxisAngle( axis, angle ) {
+
+ return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
+
+ };
+
+ }(),
+
+ applyMatrix3: function ( m ) {
+
+ var x = this.x, y = this.y, z = this.z;
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
+ this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
+ this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
+
+ return this;
+
+ },
+
+ applyMatrix4: function ( m ) {
+
+ var x = this.x, y = this.y, z = this.z;
+ var e = m.elements;
+
+ var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );
+
+ this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;
+ this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;
+ this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;
+
+ return this;
+
+ },
+
+ applyQuaternion: function ( q ) {
+
+ var x = this.x, y = this.y, z = this.z;
+ var qx = q.x, qy = q.y, qz = q.z, qw = q.w;
+
+ // calculate quat * vector
+
+ var ix = qw * x + qy * z - qz * y;
+ var iy = qw * y + qz * x - qx * z;
+ var iz = qw * z + qx * y - qy * x;
+ var iw = - qx * x - qy * y - qz * z;
+
+ // calculate result * inverse quat
+
+ this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
+ this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
+ this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
+
+ return this;
+
+ },
+
+ project: function () {
+
+ var matrix = new Matrix4();
+
+ return function project( camera ) {
+
+ matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
+ return this.applyMatrix4( matrix );
+
+ };
+
+ }(),
+
+ unproject: function () {
+
+ var matrix = new Matrix4();
+
+ return function unproject( camera ) {
+
+ matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
+ return this.applyMatrix4( matrix );
+
+ };
+
+ }(),
+
+ transformDirection: function ( m ) {
+
+ // input: THREE.Matrix4 affine matrix
+ // vector interpreted as a direction
+
+ var x = this.x, y = this.y, z = this.z;
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
+
+ return this.normalize();
+
+ },
+
+ divide: function ( v ) {
+
+ this.x /= v.x;
+ this.y /= v.y;
+ this.z /= v.z;
+
+ return this;
+
+ },
+
+ divideScalar: function ( scalar ) {
+
+ return this.multiplyScalar( 1 / scalar );
+
+ },
+
+ min: function ( v ) {
+
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+ this.z = Math.min( this.z, v.z );
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+ this.z = Math.max( this.z, v.z );
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // assumes min < max, componentwise
+
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+ this.z = Math.max( min.z, Math.min( max.z, this.z ) );
+
+ return this;
+
+ },
+
+ clampScalar: function () {
+
+ var min = new Vector3();
+ var max = new Vector3();
+
+ return function clampScalar( minVal, maxVal ) {
+
+ min.set( minVal, minVal, minVal );
+ max.set( maxVal, maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ }(),
+
+ clampLength: function ( min, max ) {
+
+ var length = this.length();
+
+ return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
+
+ },
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+ this.z = Math.floor( this.z );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+ this.z = Math.ceil( this.z );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+ this.z = Math.round( this.z );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+ this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+ this.z = - this.z;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y + this.z * v.z;
+
+ },
+
+ // TODO lengthSquared?
+
+ lengthSq: function () {
+
+ return this.x * this.x + this.y * this.y + this.z * this.z;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
+
+ },
+
+ manhattanLength: function () {
+
+ return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
+
+ },
+
+ normalize: function () {
+
+ return this.divideScalar( this.length() || 1 );
+
+ },
+
+ setLength: function ( length ) {
+
+ return this.normalize().multiplyScalar( length );
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+ this.z += ( v.z - this.z ) * alpha;
+
+ return this;
+
+ },
+
+ lerpVectors: function ( v1, v2, alpha ) {
+
+ return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+ },
+
+ cross: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
+ return this.crossVectors( v, w );
+
+ }
+
+ return this.crossVectors( this, v );
+
+ },
+
+ crossVectors: function ( a, b ) {
+
+ var ax = a.x, ay = a.y, az = a.z;
+ var bx = b.x, by = b.y, bz = b.z;
+
+ this.x = ay * bz - az * by;
+ this.y = az * bx - ax * bz;
+ this.z = ax * by - ay * bx;
+
+ return this;
+
+ },
+
+ projectOnVector: function ( vector ) {
+
+ var scalar = vector.dot( this ) / vector.lengthSq();
+
+ return this.copy( vector ).multiplyScalar( scalar );
+
+ },
+
+ projectOnPlane: function () {
+
+ var v1 = new Vector3();
+
+ return function projectOnPlane( planeNormal ) {
+
+ v1.copy( this ).projectOnVector( planeNormal );
+
+ return this.sub( v1 );
+
+ };
+
+ }(),
+
+ reflect: function () {
+
+ // reflect incident vector off plane orthogonal to normal
+ // normal is assumed to have unit length
+
+ var v1 = new Vector3();
+
+ return function reflect( normal ) {
+
+ return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
+
+ };
+
+ }(),
+
+ angleTo: function ( v ) {
+
+ var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) );
+
+ // clamp, to handle numerical problems
+
+ return Math.acos( _Math.clamp( theta, - 1, 1 ) );
+
+ },
+
+ distanceTo: function ( v ) {
+
+ return Math.sqrt( this.distanceToSquared( v ) );
+
+ },
+
+ distanceToSquared: function ( v ) {
+
+ var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
+
+ return dx * dx + dy * dy + dz * dz;
+
+ },
+
+ manhattanDistanceTo: function ( v ) {
+
+ return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );
+
+ },
+
+ setFromSpherical: function ( s ) {
+
+ var sinPhiRadius = Math.sin( s.phi ) * s.radius;
+
+ this.x = sinPhiRadius * Math.sin( s.theta );
+ this.y = Math.cos( s.phi ) * s.radius;
+ this.z = sinPhiRadius * Math.cos( s.theta );
+
+ return this;
+
+ },
+
+ setFromCylindrical: function ( c ) {
+
+ this.x = c.radius * Math.sin( c.theta );
+ this.y = c.y;
+ this.z = c.radius * Math.cos( c.theta );
+
+ return this;
+
+ },
+
+ setFromMatrixPosition: function ( m ) {
+
+ var e = m.elements;
+
+ this.x = e[ 12 ];
+ this.y = e[ 13 ];
+ this.z = e[ 14 ];
+
+ return this;
+
+ },
+
+ setFromMatrixScale: function ( m ) {
+
+ var sx = this.setFromMatrixColumn( m, 0 ).length();
+ var sy = this.setFromMatrixColumn( m, 1 ).length();
+ var sz = this.setFromMatrixColumn( m, 2 ).length();
+
+ this.x = sx;
+ this.y = sy;
+ this.z = sz;
+
+ return this;
+
+ },
+
+ setFromMatrixColumn: function ( m, index ) {
+
+ return this.fromArray( m.elements, index * 4 );
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+ this.z = array[ offset + 2 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+ array[ offset + 2 ] = this.z;
+
+ return array;
+
+ },
+
+ fromBufferAttribute: function ( attribute, index, offset ) {
+
+ if ( offset !== undefined ) {
+
+ console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' );
+
+ }
+
+ this.x = attribute.getX( index );
+ this.y = attribute.getY( index );
+ this.z = attribute.getZ( index );
+
+ return this;
+
+ }
+
+ } );
+
+ /**
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://clara.io
+ * @author tschw
+ */
+
+ function Matrix3() {
+
+ this.elements = [
+
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
+
+ ];
+
+ if ( arguments.length > 0 ) {
+
+ console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' );
+
+ }
+
+ }
+
+ Object.assign( Matrix3.prototype, {
+
+ isMatrix3: true,
+
+ set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
+
+ var te = this.elements;
+
+ te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;
+ te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;
+ te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;
+
+ return this;
+
+ },
+
+ identity: function () {
+
+ this.set(
+
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().fromArray( this.elements );
+
+ },
+
+ copy: function ( m ) {
+
+ var te = this.elements;
+ var me = m.elements;
+
+ te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ];
+ te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ];
+ te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ];
+
+ return this;
+
+ },
+
+ setFromMatrix4: function ( m ) {
+
+ var me = m.elements;
+
+ this.set(
+
+ me[ 0 ], me[ 4 ], me[ 8 ],
+ me[ 1 ], me[ 5 ], me[ 9 ],
+ me[ 2 ], me[ 6 ], me[ 10 ]
+
+ );
+
+ return this;
+
+ },
+
+ applyToBufferAttribute: function () {
+
+ var v1 = new Vector3();
+
+ return function applyToBufferAttribute( attribute ) {
+
+ for ( var i = 0, l = attribute.count; i < l; i ++ ) {
+
+ v1.x = attribute.getX( i );
+ v1.y = attribute.getY( i );
+ v1.z = attribute.getZ( i );
+
+ v1.applyMatrix3( this );
+
+ attribute.setXYZ( i, v1.x, v1.y, v1.z );
+
+ }
+
+ return attribute;
+
+ };
+
+ }(),
+
+ multiply: function ( m ) {
+
+ return this.multiplyMatrices( this, m );
+
+ },
+
+ premultiply: function ( m ) {
+
+ return this.multiplyMatrices( m, this );
+
+ },
+
+ multiplyMatrices: function ( a, b ) {
+
+ var ae = a.elements;
+ var be = b.elements;
+ var te = this.elements;
+
+ var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];
+ var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];
+ var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];
+
+ var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];
+ var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];
+ var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];
+
+ te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;
+ te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;
+ te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;
+
+ te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;
+ te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;
+ te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;
+
+ te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;
+ te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;
+ te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( s ) {
+
+ var te = this.elements;
+
+ te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
+ te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
+ te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;
+
+ return this;
+
+ },
+
+ determinant: function () {
+
+ var te = this.elements;
+
+ var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
+ d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
+ g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];
+
+ return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;
+
+ },
+
+ getInverse: function ( matrix, throwOnDegenerate ) {
+
+ if ( matrix && matrix.isMatrix4 ) {
+
+ console.error( "THREE.Matrix3: .getInverse() no longer takes a Matrix4 argument." );
+
+ }
+
+ var me = matrix.elements,
+ te = this.elements,
+
+ n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ],
+ n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ],
+ n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ],
+
+ t11 = n33 * n22 - n32 * n23,
+ t12 = n32 * n13 - n33 * n12,
+ t13 = n23 * n12 - n22 * n13,
+
+ det = n11 * t11 + n21 * t12 + n31 * t13;
+
+ if ( det === 0 ) {
+
+ var msg = "THREE.Matrix3: .getInverse() can't invert matrix, determinant is 0";
+
+ if ( throwOnDegenerate === true ) {
+
+ throw new Error( msg );
+
+ } else {
+
+ console.warn( msg );
+
+ }
+
+ return this.identity();
+
+ }
+
+ var detInv = 1 / det;
+
+ te[ 0 ] = t11 * detInv;
+ te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;
+ te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;
+
+ te[ 3 ] = t12 * detInv;
+ te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;
+ te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;
+
+ te[ 6 ] = t13 * detInv;
+ te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;
+ te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;
+
+ return this;
+
+ },
+
+ transpose: function () {
+
+ var tmp, m = this.elements;
+
+ tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
+ tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
+ tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;
+
+ return this;
+
+ },
+
+ getNormalMatrix: function ( matrix4 ) {
+
+ return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose();
+
+ },
+
+ transposeIntoArray: function ( r ) {
+
+ var m = this.elements;
+
+ r[ 0 ] = m[ 0 ];
+ r[ 1 ] = m[ 3 ];
+ r[ 2 ] = m[ 6 ];
+ r[ 3 ] = m[ 1 ];
+ r[ 4 ] = m[ 4 ];
+ r[ 5 ] = m[ 7 ];
+ r[ 6 ] = m[ 2 ];
+ r[ 7 ] = m[ 5 ];
+ r[ 8 ] = m[ 8 ];
+
+ return this;
+
+ },
+
+ setUvTransform: function ( tx, ty, sx, sy, rotation, cx, cy ) {
+
+ var c = Math.cos( rotation );
+ var s = Math.sin( rotation );
+
+ this.set(
+ sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx,
+ - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty,
+ 0, 0, 1
+ );
+
+ },
+
+ scale: function ( sx, sy ) {
+
+ var te = this.elements;
+
+ te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx;
+ te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy;
+
+ return this;
+
+ },
+
+ rotate: function ( theta ) {
+
+ var c = Math.cos( theta );
+ var s = Math.sin( theta );
+
+ var te = this.elements;
+
+ var a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ];
+ var a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ];
+
+ te[ 0 ] = c * a11 + s * a21;
+ te[ 3 ] = c * a12 + s * a22;
+ te[ 6 ] = c * a13 + s * a23;
+
+ te[ 1 ] = - s * a11 + c * a21;
+ te[ 4 ] = - s * a12 + c * a22;
+ te[ 7 ] = - s * a13 + c * a23;
+
+ return this;
+
+ },
+
+ translate: function ( tx, ty ) {
+
+ var te = this.elements;
+
+ te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ];
+ te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ];
+
+ return this;
+
+ },
+
+ equals: function ( matrix ) {
+
+ var te = this.elements;
+ var me = matrix.elements;
+
+ for ( var i = 0; i < 9; i ++ ) {
+
+ if ( te[ i ] !== me[ i ] ) return false;
+
+ }
+
+ return true;
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ for ( var i = 0; i < 9; i ++ ) {
+
+ this.elements[ i ] = array[ i + offset ];
+
+ }
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ var te = this.elements;
+
+ array[ offset ] = te[ 0 ];
+ array[ offset + 1 ] = te[ 1 ];
+ array[ offset + 2 ] = te[ 2 ];
+
+ array[ offset + 3 ] = te[ 3 ];
+ array[ offset + 4 ] = te[ 4 ];
+ array[ offset + 5 ] = te[ 5 ];
+
+ array[ offset + 6 ] = te[ 6 ];
+ array[ offset + 7 ] = te[ 7 ];
+ array[ offset + 8 ] = te[ 8 ];
+
+ return array;
+
+ }
+
+ } );
+
+ /**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author szimek / https://github.com/szimek/
+ */
+
+ var textureId = 0;
+
+ function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) {
+
+ Object.defineProperty( this, 'id', { value: textureId ++ } );
+
+ this.uuid = _Math.generateUUID();
+
+ this.name = '';
+
+ this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE;
+ this.mipmaps = [];
+
+ this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING;
+
+ this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping;
+ this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping;
+
+ this.magFilter = magFilter !== undefined ? magFilter : LinearFilter;
+ this.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter;
+
+ this.anisotropy = anisotropy !== undefined ? anisotropy : 1;
+
+ this.format = format !== undefined ? format : RGBAFormat;
+ this.type = type !== undefined ? type : UnsignedByteType;
+
+ this.offset = new Vector2( 0, 0 );
+ this.repeat = new Vector2( 1, 1 );
+ this.center = new Vector2( 0, 0 );
+ this.rotation = 0;
+
+ this.matrixAutoUpdate = true;
+ this.matrix = new Matrix3();
+
+ this.generateMipmaps = true;
+ this.premultiplyAlpha = false;
+ this.flipY = true;
+ this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
+
+ // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap.
+ //
+ // Also changing the encoding after already used by a Material will not automatically make the Material
+ // update. You need to explicitly call Material.needsUpdate to trigger it to recompile.
+ this.encoding = encoding !== undefined ? encoding : LinearEncoding;
+
+ this.version = 0;
+ this.onUpdate = null;
+
+ }
+
+ Texture.DEFAULT_IMAGE = undefined;
+ Texture.DEFAULT_MAPPING = UVMapping;
+
+ Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
+
+ constructor: Texture,
+
+ isTexture: true,
+
+ updateMatrix: function () {
+
+ this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y );
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.name = source.name;
+
+ this.image = source.image;
+ this.mipmaps = source.mipmaps.slice( 0 );
+
+ this.mapping = source.mapping;
+
+ this.wrapS = source.wrapS;
+ this.wrapT = source.wrapT;
+
+ this.magFilter = source.magFilter;
+ this.minFilter = source.minFilter;
+
+ this.anisotropy = source.anisotropy;
+
+ this.format = source.format;
+ this.type = source.type;
+
+ this.offset.copy( source.offset );
+ this.repeat.copy( source.repeat );
+ this.center.copy( source.center );
+ this.rotation = source.rotation;
+
+ this.matrixAutoUpdate = source.matrixAutoUpdate;
+ this.matrix.copy( source.matrix );
+
+ this.generateMipmaps = source.generateMipmaps;
+ this.premultiplyAlpha = source.premultiplyAlpha;
+ this.flipY = source.flipY;
+ this.unpackAlignment = source.unpackAlignment;
+ this.encoding = source.encoding;
+
+ return this;
+
+ },
+
+ toJSON: function ( meta ) {
+
+ var isRootObject = ( meta === undefined || typeof meta === 'string' );
+
+ if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) {
+
+ return meta.textures[ this.uuid ];
+
+ }
+
+ function getDataURL( image ) {
+
+ var canvas;
+
+ if ( image instanceof HTMLCanvasElement ) {
+
+ canvas = image;
+
+ } else {
+
+ canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
+ canvas.width = image.width;
+ canvas.height = image.height;
+
+ var context = canvas.getContext( '2d' );
+
+ if ( image instanceof ImageData ) {
+
+ context.putImageData( image, 0, 0 );
+
+ } else {
+
+ context.drawImage( image, 0, 0, image.width, image.height );
+
+ }
+
+ }
+
+ if ( canvas.width > 2048 || canvas.height > 2048 ) {
+
+ return canvas.toDataURL( 'image/jpeg', 0.6 );
+
+ } else {
+
+ return canvas.toDataURL( 'image/png' );
+
+ }
+
+ }
+
+ var output = {
+
+ metadata: {
+ version: 4.5,
+ type: 'Texture',
+ generator: 'Texture.toJSON'
+ },
+
+ uuid: this.uuid,
+ name: this.name,
+
+ mapping: this.mapping,
+
+ repeat: [ this.repeat.x, this.repeat.y ],
+ offset: [ this.offset.x, this.offset.y ],
+ center: [ this.center.x, this.center.y ],
+ rotation: this.rotation,
+
+ wrap: [ this.wrapS, this.wrapT ],
+
+ format: this.format,
+ minFilter: this.minFilter,
+ magFilter: this.magFilter,
+ anisotropy: this.anisotropy,
+
+ flipY: this.flipY
+
+ };
+
+ if ( this.image !== undefined ) {
+
+ // TODO: Move to THREE.Image
+
+ var image = this.image;
+
+ if ( image.uuid === undefined ) {
+
+ image.uuid = _Math.generateUUID(); // UGH
+
+ }
+
+ if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) {
+
+ meta.images[ image.uuid ] = {
+ uuid: image.uuid,
+ url: getDataURL( image )
+ };
+
+ }
+
+ output.image = image.uuid;
+
+ }
+
+ if ( ! isRootObject ) {
+
+ meta.textures[ this.uuid ] = output;
+
+ }
+
+ return output;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ },
+
+ transformUv: function ( uv ) {
+
+ if ( this.mapping !== UVMapping ) return;
+
+ uv.applyMatrix3( this.matrix );
+
+ if ( uv.x < 0 || uv.x > 1 ) {
+
+ switch ( this.wrapS ) {
+
+ case RepeatWrapping:
+
+ uv.x = uv.x - Math.floor( uv.x );
+ break;
+
+ case ClampToEdgeWrapping:
+
+ uv.x = uv.x < 0 ? 0 : 1;
+ break;
+
+ case MirroredRepeatWrapping:
+
+ if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {
+
+ uv.x = Math.ceil( uv.x ) - uv.x;
+
+ } else {
+
+ uv.x = uv.x - Math.floor( uv.x );
+
+ }
+ break;
+
+ }
+
+ }
+
+ if ( uv.y < 0 || uv.y > 1 ) {
+
+ switch ( this.wrapT ) {
+
+ case RepeatWrapping:
+
+ uv.y = uv.y - Math.floor( uv.y );
+ break;
+
+ case ClampToEdgeWrapping:
+
+ uv.y = uv.y < 0 ? 0 : 1;
+ break;
+
+ case MirroredRepeatWrapping:
+
+ if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {
+
+ uv.y = Math.ceil( uv.y ) - uv.y;
+
+ } else {
+
+ uv.y = uv.y - Math.floor( uv.y );
+
+ }
+ break;
+
+ }
+
+ }
+
+ if ( this.flipY ) {
+
+ uv.y = 1 - uv.y;
+
+ }
+
+ }
+
+ } );
+
+ Object.defineProperty( Texture.prototype, "needsUpdate", {
+
+ set: function ( value ) {
+
+ if ( value === true ) this.version ++;
+
+ }
+
+ } );
+
+ /**
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author philogb / http://blog.thejit.org/
+ * @author mikael emtinger / http://gomo.se/
+ * @author egraether / http://egraether.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+ function Vector4( x, y, z, w ) {
+
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+ this.w = ( w !== undefined ) ? w : 1;
+
+ }
+
+ Object.assign( Vector4.prototype, {
+
+ isVector4: true,
+
+ set: function ( x, y, z, w ) {
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+
+ return this;
+
+ },
+
+ setScalar: function ( scalar ) {
+
+ this.x = scalar;
+ this.y = scalar;
+ this.z = scalar;
+ this.w = scalar;
+
+ return this;
+
+ },
+
+ setX: function ( x ) {
+
+ this.x = x;
+
+ return this;
+
+ },
+
+ setY: function ( y ) {
+
+ this.y = y;
+
+ return this;
+
+ },
+
+ setZ: function ( z ) {
+
+ this.z = z;
+
+ return this;
+
+ },
+
+ setW: function ( w ) {
+
+ this.w = w;
+
+ return this;
+
+ },
+
+ setComponent: function ( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ case 2: this.z = value; break;
+ case 3: this.w = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ return this;
+
+ },
+
+ getComponent: function ( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ case 2: return this.z;
+ case 3: return this.w;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this.x, this.y, this.z, this.w );
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+ this.w = ( v.w !== undefined ) ? v.w : 1;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+ this.z += v.z;
+ this.w += v.w;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+ this.z += s;
+ this.w += s;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+ this.z = a.z + b.z;
+ this.w = a.w + b.w;
+
+ return this;
+
+ },
+
+ addScaledVector: function ( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+ this.z += v.z * s;
+ this.w += v.w * s;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+ this.z -= v.z;
+ this.w -= v.w;
+
+ return this;
+
+ },
+
+ subScalar: function ( s ) {
+
+ this.x -= s;
+ this.y -= s;
+ this.z -= s;
+ this.w -= s;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+ this.z = a.z - b.z;
+ this.w = a.w - b.w;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( scalar ) {
+
+ this.x *= scalar;
+ this.y *= scalar;
+ this.z *= scalar;
+ this.w *= scalar;
+
+ return this;
+
+ },
+
+ applyMatrix4: function ( m ) {
+
+ var x = this.x, y = this.y, z = this.z, w = this.w;
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;
+ this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;
+
+ return this;
+
+ },
+
+ divideScalar: function ( scalar ) {
+
+ return this.multiplyScalar( 1 / scalar );
+
+ },
+
+ setAxisAngleFromQuaternion: function ( q ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
+
+ // q is assumed to be normalized
+
+ this.w = 2 * Math.acos( q.w );
+
+ var s = Math.sqrt( 1 - q.w * q.w );
+
+ if ( s < 0.0001 ) {
+
+ this.x = 1;
+ this.y = 0;
+ this.z = 0;
+
+ } else {
+
+ this.x = q.x / s;
+ this.y = q.y / s;
+ this.z = q.z / s;
+
+ }
+
+ return this;
+
+ },
+
+ setAxisAngleFromRotationMatrix: function ( m ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ var angle, x, y, z, // variables for result
+ epsilon = 0.01, // margin to allow for rounding errors
+ epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees
+
+ te = m.elements,
+
+ m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+ m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+ m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
+
+ if ( ( Math.abs( m12 - m21 ) < epsilon ) &&
+ ( Math.abs( m13 - m31 ) < epsilon ) &&
+ ( Math.abs( m23 - m32 ) < epsilon ) ) {
+
+ // singularity found
+ // first check for identity matrix which must have +1 for all terms
+ // in leading diagonal and zero in other terms
+
+ if ( ( Math.abs( m12 + m21 ) < epsilon2 ) &&
+ ( Math.abs( m13 + m31 ) < epsilon2 ) &&
+ ( Math.abs( m23 + m32 ) < epsilon2 ) &&
+ ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {
+
+ // this singularity is identity matrix so angle = 0
+
+ this.set( 1, 0, 0, 0 );
+
+ return this; // zero angle, arbitrary axis
+
+ }
+
+ // otherwise this singularity is angle = 180
+
+ angle = Math.PI;
+
+ var xx = ( m11 + 1 ) / 2;
+ var yy = ( m22 + 1 ) / 2;
+ var zz = ( m33 + 1 ) / 2;
+ var xy = ( m12 + m21 ) / 4;
+ var xz = ( m13 + m31 ) / 4;
+ var yz = ( m23 + m32 ) / 4;
+
+ if ( ( xx > yy ) && ( xx > zz ) ) {
+
+ // m11 is the largest diagonal term
+
+ if ( xx < epsilon ) {
+
+ x = 0;
+ y = 0.707106781;
+ z = 0.707106781;
+
+ } else {
+
+ x = Math.sqrt( xx );
+ y = xy / x;
+ z = xz / x;
+
+ }
+
+ } else if ( yy > zz ) {
+
+ // m22 is the largest diagonal term
+
+ if ( yy < epsilon ) {
+
+ x = 0.707106781;
+ y = 0;
+ z = 0.707106781;
+
+ } else {
+
+ y = Math.sqrt( yy );
+ x = xy / y;
+ z = yz / y;
+
+ }
+
+ } else {
+
+ // m33 is the largest diagonal term so base result on this
+
+ if ( zz < epsilon ) {
+
+ x = 0.707106781;
+ y = 0.707106781;
+ z = 0;
+
+ } else {
+
+ z = Math.sqrt( zz );
+ x = xz / z;
+ y = yz / z;
+
+ }
+
+ }
+
+ this.set( x, y, z, angle );
+
+ return this; // return 180 deg rotation
+
+ }
+
+ // as we have reached here there are no singularities so we can handle normally
+
+ var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) +
+ ( m13 - m31 ) * ( m13 - m31 ) +
+ ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize
+
+ if ( Math.abs( s ) < 0.001 ) s = 1;
+
+ // prevent divide by zero, should not happen if matrix is orthogonal and should be
+ // caught by singularity test above, but I've left it in just in case
+
+ this.x = ( m32 - m23 ) / s;
+ this.y = ( m13 - m31 ) / s;
+ this.z = ( m21 - m12 ) / s;
+ this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );
+
+ return this;
+
+ },
+
+ min: function ( v ) {
+
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+ this.z = Math.min( this.z, v.z );
+ this.w = Math.min( this.w, v.w );
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+ this.z = Math.max( this.z, v.z );
+ this.w = Math.max( this.w, v.w );
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // assumes min < max, componentwise
+
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+ this.z = Math.max( min.z, Math.min( max.z, this.z ) );
+ this.w = Math.max( min.w, Math.min( max.w, this.w ) );
+
+ return this;
+
+ },
+
+ clampScalar: function () {
+
+ var min, max;
+
+ return function clampScalar( minVal, maxVal ) {
+
+ if ( min === undefined ) {
+
+ min = new Vector4();
+ max = new Vector4();
+
+ }
+
+ min.set( minVal, minVal, minVal, minVal );
+ max.set( maxVal, maxVal, maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ }(),
+
+ clampLength: function ( min, max ) {
+
+ var length = this.length();
+
+ return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
+
+ },
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+ this.z = Math.floor( this.z );
+ this.w = Math.floor( this.w );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+ this.z = Math.ceil( this.z );
+ this.w = Math.ceil( this.w );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+ this.z = Math.round( this.z );
+ this.w = Math.round( this.w );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+ this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
+ this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+ this.z = - this.z;
+ this.w = - this.w;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;
+
+ },
+
+ lengthSq: function () {
+
+ return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );
+
+ },
+
+ manhattanLength: function () {
+
+ return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );
+
+ },
+
+ normalize: function () {
+
+ return this.divideScalar( this.length() || 1 );
+
+ },
+
+ setLength: function ( length ) {
+
+ return this.normalize().multiplyScalar( length );
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+ this.z += ( v.z - this.z ) * alpha;
+ this.w += ( v.w - this.w ) * alpha;
+
+ return this;
+
+ },
+
+ lerpVectors: function ( v1, v2, alpha ) {
+
+ return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+ this.z = array[ offset + 2 ];
+ this.w = array[ offset + 3 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+ array[ offset + 2 ] = this.z;
+ array[ offset + 3 ] = this.w;
+
+ return array;
+
+ },
+
+ fromBufferAttribute: function ( attribute, index, offset ) {
+
+ if ( offset !== undefined ) {
+
+ console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' );
+
+ }
+
+ this.x = attribute.getX( index );
+ this.y = attribute.getY( index );
+ this.z = attribute.getZ( index );
+ this.w = attribute.getW( index );
+
+ return this;
+
+ }
+
+ } );
+
+ /**
+ * @author szimek / https://github.com/szimek/
+ * @author alteredq / http://alteredqualia.com/
+ * @author Marius Kintel / https://github.com/kintel
+ */
+
+ /*
+ In options, we can specify:
+ * Texture parameters for an auto-generated target texture
+ * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers
+ */
+ function WebGLRenderTarget( width, height, options ) {
+
+ this.width = width;
+ this.height = height;
+
+ this.scissor = new Vector4( 0, 0, width, height );
+ this.scissorTest = false;
+
+ this.viewport = new Vector4( 0, 0, width, height );
+
+ options = options || {};
+
+ if ( options.minFilter === undefined ) options.minFilter = LinearFilter;
+
+ this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding );
+
+ this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : true;
+
+ this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
+ this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true;
+ this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null;
+
+ }
+
+ WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
+
+ constructor: WebGLRenderTarget,
+
+ isWebGLRenderTarget: true,
+
+ setSize: function ( width, height ) {
+
+ if ( this.width !== width || this.height !== height ) {
+
+ this.width = width;
+ this.height = height;
+
+ this.dispose();
+
+ }
+
+ this.viewport.set( 0, 0, width, height );
+ this.scissor.set( 0, 0, width, height );
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.width = source.width;
+ this.height = source.height;
+
+ this.viewport.copy( source.viewport );
+
+ this.texture = source.texture.clone();
+
+ this.depthBuffer = source.depthBuffer;
+ this.stencilBuffer = source.stencilBuffer;
+ this.depthTexture = source.depthTexture;
+
+ return this;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ }
+
+ } );
+
+ /**
+ * @author alteredq / http://alteredqualia.com
+ */
+
+ function WebGLRenderTargetCube( width, height, options ) {
+
+ WebGLRenderTarget.call( this, width, height, options );
+
+ this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5
+ this.activeMipMapLevel = 0;
+
+ }
+
+ WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype );
+ WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube;
+
+ WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true;
+
+ /**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+ function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) {
+
+ Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );
+
+ this.image = { data: data, width: width, height: height };
+
+ this.magFilter = magFilter !== undefined ? magFilter : NearestFilter;
+ this.minFilter = minFilter !== undefined ? minFilter : NearestFilter;
+
+ this.generateMipmaps = false;
+ this.flipY = false;
+ this.unpackAlignment = 1;
+
+ }
+
+ DataTexture.prototype = Object.create( Texture.prototype );
+ DataTexture.prototype.constructor = DataTexture;
+
+ DataTexture.prototype.isDataTexture = true;
+
+ /**
+ * @author bhouston / http://clara.io
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+ function Box3( min, max ) {
+
+ this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity );
+ this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity );
+
+ }
+
+ Object.assign( Box3.prototype, {
+
+ isBox3: true,
+
+ set: function ( min, max ) {
+
+ this.min.copy( min );
+ this.max.copy( max );
+
+ return this;
+
+ },
+
+ setFromArray: function ( array ) {
+
+ var minX = + Infinity;
+ var minY = + Infinity;
+ var minZ = + Infinity;
+
+ var maxX = - Infinity;
+ var maxY = - Infinity;
+ var maxZ = - Infinity;
+
+ for ( var i = 0, l = array.length; i < l; i += 3 ) {
+
+ var x = array[ i ];
+ var y = array[ i + 1 ];
+ var z = array[ i + 2 ];
+
+ if ( x < minX ) minX = x;
+ if ( y < minY ) minY = y;
+ if ( z < minZ ) minZ = z;
+
+ if ( x > maxX ) maxX = x;
+ if ( y > maxY ) maxY = y;
+ if ( z > maxZ ) maxZ = z;
+
+ }
+
+ this.min.set( minX, minY, minZ );
+ this.max.set( maxX, maxY, maxZ );
+
+ return this;
+
+ },
+
+ setFromBufferAttribute: function ( attribute ) {
+
+ var minX = + Infinity;
+ var minY = + Infinity;
+ var minZ = + Infinity;
+
+ var maxX = - Infinity;
+ var maxY = - Infinity;
+ var maxZ = - Infinity;
+
+ for ( var i = 0, l = attribute.count; i < l; i ++ ) {
+
+ var x = attribute.getX( i );
+ var y = attribute.getY( i );
+ var z = attribute.getZ( i );
+
+ if ( x < minX ) minX = x;
+ if ( y < minY ) minY = y;
+ if ( z < minZ ) minZ = z;
+
+ if ( x > maxX ) maxX = x;
+ if ( y > maxY ) maxY = y;
+ if ( z > maxZ ) maxZ = z;
+
+ }
+
+ this.min.set( minX, minY, minZ );
+ this.max.set( maxX, maxY, maxZ );
+
+ return this;
+
+ },
+
+ setFromPoints: function ( points ) {
+
+ this.makeEmpty();
+
+ for ( var i = 0, il = points.length; i < il; i ++ ) {
+
+ this.expandByPoint( points[ i ] );
+
+ }
+
+ return this;
+
+ },
+
+ setFromCenterAndSize: function () {
+
+ var v1 = new Vector3();
+
+ return function setFromCenterAndSize( center, size ) {
+
+ var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
+
+ this.min.copy( center ).sub( halfSize );
+ this.max.copy( center ).add( halfSize );
+
+ return this;
+
+ };
+
+ }(),
+
+ setFromObject: function ( object ) {
+
+ this.makeEmpty();
+
+ return this.expandByObject( object );
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( box ) {
+
+ this.min.copy( box.min );
+ this.max.copy( box.max );
+
+ return this;
+
+ },
+
+ makeEmpty: function () {
+
+ this.min.x = this.min.y = this.min.z = + Infinity;
+ this.max.x = this.max.y = this.max.z = - Infinity;
+
+ return this;
+
+ },
+
+ isEmpty: function () {
+
+ // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
+
+ return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );
+
+ },
+
+ getCenter: function ( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Box3: .getCenter() target is now required' );
+ target = new Vector3();
+
+ }
+
+ return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
+
+ },
+
+ getSize: function ( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Box3: .getSize() target is now required' );
+ target = new Vector3();
+
+ }
+
+ return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min );
+
+ },
+
+ expandByPoint: function ( point ) {
+
+ this.min.min( point );
+ this.max.max( point );
+
+ return this;
+
+ },
+
+ expandByVector: function ( vector ) {
+
+ this.min.sub( vector );
+ this.max.add( vector );
+
+ return this;
+
+ },
+
+ expandByScalar: function ( scalar ) {
+
+ this.min.addScalar( - scalar );
+ this.max.addScalar( scalar );
+
+ return this;
+
+ },
+
+ expandByObject: function () {
+
+ // Computes the world-axis-aligned bounding box of an object (including its children),
+ // accounting for both the object's, and children's, world transforms
+
+ var scope, i, l;
+
+ var v1 = new Vector3();
+
+ function traverse( node ) {
+
+ var geometry = node.geometry;
+
+ if ( geometry !== undefined ) {
+
+ if ( geometry.isGeometry ) {
+
+ var vertices = geometry.vertices;
+
+ for ( i = 0, l = vertices.length; i < l; i ++ ) {
+
+ v1.copy( vertices[ i ] );
+ v1.applyMatrix4( node.matrixWorld );
+
+ scope.expandByPoint( v1 );
+
+ }
+
+ } else if ( geometry.isBufferGeometry ) {
+
+ var attribute = geometry.attributes.position;
+
+ if ( attribute !== undefined ) {
+
+ for ( i = 0, l = attribute.count; i < l; i ++ ) {
+
+ v1.fromBufferAttribute( attribute, i ).applyMatrix4( node.matrixWorld );
+
+ scope.expandByPoint( v1 );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ return function expandByObject( object ) {
+
+ scope = this;
+
+ object.updateMatrixWorld( true );
+
+ object.traverse( traverse );
+
+ return this;
+
+ };
+
+ }(),
+
+ containsPoint: function ( point ) {
+
+ return point.x < this.min.x || point.x > this.max.x ||
+ point.y < this.min.y || point.y > this.max.y ||
+ point.z < this.min.z || point.z > this.max.z ? false : true;
+
+ },
+
+ containsBox: function ( box ) {
+
+ return this.min.x <= box.min.x && box.max.x <= this.max.x &&
+ this.min.y <= box.min.y && box.max.y <= this.max.y &&
+ this.min.z <= box.min.z && box.max.z <= this.max.z;
+
+ },
+
+ getParameter: function ( point, target ) {
+
+ // This can potentially have a divide by zero if the box
+ // has a size dimension of 0.
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Box3: .getParameter() target is now required' );
+ target = new Vector3();
+
+ }
+
+ return target.set(
+ ( point.x - this.min.x ) / ( this.max.x - this.min.x ),
+ ( point.y - this.min.y ) / ( this.max.y - this.min.y ),
+ ( point.z - this.min.z ) / ( this.max.z - this.min.z )
+ );
+
+ },
+
+ intersectsBox: function ( box ) {
+
+ // using 6 splitting planes to rule out intersections.
+ return box.max.x < this.min.x || box.min.x > this.max.x ||
+ box.max.y < this.min.y || box.min.y > this.max.y ||
+ box.max.z < this.min.z || box.min.z > this.max.z ? false : true;
+
+ },
+
+ intersectsSphere: ( function () {
+
+ var closestPoint = new Vector3();
+
+ return function intersectsSphere( sphere ) {
+
+ // Find the point on the AABB closest to the sphere center.
+ this.clampPoint( sphere.center, closestPoint );
+
+ // If that point is inside the sphere, the AABB and sphere intersect.
+ return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius );
+
+ };
+
+ } )(),
+
+ intersectsPlane: function ( plane ) {
+
+ // We compute the minimum and maximum dot product values. If those values
+ // are on the same side (back or front) of the plane, then there is no intersection.
+
+ var min, max;
+
+ if ( plane.normal.x > 0 ) {
+
+ min = plane.normal.x * this.min.x;
+ max = plane.normal.x * this.max.x;
+
+ } else {
+
+ min = plane.normal.x * this.max.x;
+ max = plane.normal.x * this.min.x;
+
+ }
+
+ if ( plane.normal.y > 0 ) {
+
+ min += plane.normal.y * this.min.y;
+ max += plane.normal.y * this.max.y;
+
+ } else {
+
+ min += plane.normal.y * this.max.y;
+ max += plane.normal.y * this.min.y;
+
+ }
+
+ if ( plane.normal.z > 0 ) {
+
+ min += plane.normal.z * this.min.z;
+ max += plane.normal.z * this.max.z;
+
+ } else {
+
+ min += plane.normal.z * this.max.z;
+ max += plane.normal.z * this.min.z;
+
+ }
+
+ return ( min <= plane.constant && max >= plane.constant );
+
+ },
+
+ intersectsTriangle: ( function () {
+
+ // triangle centered vertices
+ var v0 = new Vector3();
+ var v1 = new Vector3();
+ var v2 = new Vector3();
+
+ // triangle edge vectors
+ var f0 = new Vector3();
+ var f1 = new Vector3();
+ var f2 = new Vector3();
+
+ var testAxis = new Vector3();
+
+ var center = new Vector3();
+ var extents = new Vector3();
+
+ var triangleNormal = new Vector3();
+
+ function satForAxes( axes ) {
+
+ var i, j;
+
+ for ( i = 0, j = axes.length - 3; i <= j; i += 3 ) {
+
+ testAxis.fromArray( axes, i );
+ // project the aabb onto the seperating axis
+ var r = extents.x * Math.abs( testAxis.x ) + extents.y * Math.abs( testAxis.y ) + extents.z * Math.abs( testAxis.z );
+ // project all 3 vertices of the triangle onto the seperating axis
+ var p0 = v0.dot( testAxis );
+ var p1 = v1.dot( testAxis );
+ var p2 = v2.dot( testAxis );
+ // actual test, basically see if either of the most extreme of the triangle points intersects r
+ if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {
+
+ // points of the projected triangle are outside the projected half-length of the aabb
+ // the axis is seperating and we can exit
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+ }
+
+ return function intersectsTriangle( triangle ) {
+
+ if ( this.isEmpty() ) {
+
+ return false;
+
+ }
+
+ // compute box center and extents
+ this.getCenter( center );
+ extents.subVectors( this.max, center );
+
+ // translate triangle to aabb origin
+ v0.subVectors( triangle.a, center );
+ v1.subVectors( triangle.b, center );
+ v2.subVectors( triangle.c, center );
+
+ // compute edge vectors for triangle
+ f0.subVectors( v1, v0 );
+ f1.subVectors( v2, v1 );
+ f2.subVectors( v0, v2 );
+
+ // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb
+ // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation
+ // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned)
+ var axes = [
+ 0, - f0.z, f0.y, 0, - f1.z, f1.y, 0, - f2.z, f2.y,
+ f0.z, 0, - f0.x, f1.z, 0, - f1.x, f2.z, 0, - f2.x,
+ - f0.y, f0.x, 0, - f1.y, f1.x, 0, - f2.y, f2.x, 0
+ ];
+ if ( ! satForAxes( axes ) ) {
+
+ return false;
+
+ }
+
+ // test 3 face normals from the aabb
+ axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ];
+ if ( ! satForAxes( axes ) ) {
+
+ return false;
+
+ }
+
+ // finally testing the face normal of the triangle
+ // use already existing triangle edge vectors here
+ triangleNormal.crossVectors( f0, f1 );
+ axes = [ triangleNormal.x, triangleNormal.y, triangleNormal.z ];
+ return satForAxes( axes );
+
+ };
+
+ } )(),
+
+ clampPoint: function ( point, target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Box3: .clampPoint() target is now required' );
+ target = new Vector3();
+
+ }
+
+ return target.copy( point ).clamp( this.min, this.max );
+
+ },
+
+ distanceToPoint: function () {
+
+ var v1 = new Vector3();
+
+ return function distanceToPoint( point ) {
+
+ var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
+ return clampedPoint.sub( point ).length();
+
+ };
+
+ }(),
+
+ getBoundingSphere: function () {
+
+ var v1 = new Vector3();
+
+ return function getBoundingSphere( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Box3: .getBoundingSphere() target is now required' );
+ target = new Sphere();
+
+ }
+
+ this.getCenter( target.center );
+
+ target.radius = this.getSize( v1 ).length() * 0.5;
+
+ return target;
+
+ };
+
+ }(),
+
+ intersect: function ( box ) {
+
+ this.min.max( box.min );
+ this.max.min( box.max );
+
+ // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values.
+ if ( this.isEmpty() ) this.makeEmpty();
+
+ return this;
+
+ },
+
+ union: function ( box ) {
+
+ this.min.min( box.min );
+ this.max.max( box.max );
+
+ return this;
+
+ },
+
+ applyMatrix4: function ( matrix ) {
+
+ // transform of empty box is an empty box.
+ if ( this.isEmpty( ) ) return this;
+
+ var m = matrix.elements;
+
+ var xax = m[ 0 ] * this.min.x, xay = m[ 1 ] * this.min.x, xaz = m[ 2 ] * this.min.x;
+ var xbx = m[ 0 ] * this.max.x, xby = m[ 1 ] * this.max.x, xbz = m[ 2 ] * this.max.x;
+ var yax = m[ 4 ] * this.min.y, yay = m[ 5 ] * this.min.y, yaz = m[ 6 ] * this.min.y;
+ var ybx = m[ 4 ] * this.max.y, yby = m[ 5 ] * this.max.y, ybz = m[ 6 ] * this.max.y;
+ var zax = m[ 8 ] * this.min.z, zay = m[ 9 ] * this.min.z, zaz = m[ 10 ] * this.min.z;
+ var zbx = m[ 8 ] * this.max.z, zby = m[ 9 ] * this.max.z, zbz = m[ 10 ] * this.max.z;
+
+ this.min.x = Math.min( xax, xbx ) + Math.min( yax, ybx ) + Math.min( zax, zbx ) + m[ 12 ];
+ this.min.y = Math.min( xay, xby ) + Math.min( yay, yby ) + Math.min( zay, zby ) + m[ 13 ];
+ this.min.z = Math.min( xaz, xbz ) + Math.min( yaz, ybz ) + Math.min( zaz, zbz ) + m[ 14 ];
+ this.max.x = Math.max( xax, xbx ) + Math.max( yax, ybx ) + Math.max( zax, zbx ) + m[ 12 ];
+ this.max.y = Math.max( xay, xby ) + Math.max( yay, yby ) + Math.max( zay, zby ) + m[ 13 ];
+ this.max.z = Math.max( xaz, xbz ) + Math.max( yaz, ybz ) + Math.max( zaz, zbz ) + m[ 14 ];
+
+ return this;
+
+ },
+
+ translate: function ( offset ) {
+
+ this.min.add( offset );
+ this.max.add( offset );
+
+ return this;
+
+ },
+
+ equals: function ( box ) {
+
+ return box.min.equals( this.min ) && box.max.equals( this.max );
+
+ }
+
+ } );
+
+ /**
+ * @author bhouston / http://clara.io
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+ function Sphere( center, radius ) {
+
+ this.center = ( center !== undefined ) ? center : new Vector3();
+ this.radius = ( radius !== undefined ) ? radius : 0;
+
+ }
+
+ Object.assign( Sphere.prototype, {
+
+ set: function ( center, radius ) {
+
+ this.center.copy( center );
+ this.radius = radius;
+
+ return this;
+
+ },
+
+ setFromPoints: function () {
+
+ var box = new Box3();
+
+ return function setFromPoints( points, optionalCenter ) {
+
+ var center = this.center;
+
+ if ( optionalCenter !== undefined ) {
+
+ center.copy( optionalCenter );
+
+ } else {
+
+ box.setFromPoints( points ).getCenter( center );
+
+ }
+
+ var maxRadiusSq = 0;
+
+ for ( var i = 0, il = points.length; i < il; i ++ ) {
+
+ maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) );
+
+ }
+
+ this.radius = Math.sqrt( maxRadiusSq );
+
+ return this;
+
+ };
+
+ }(),
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( sphere ) {
+
+ this.center.copy( sphere.center );
+ this.radius = sphere.radius;
+
+ return this;
+
+ },
+
+ empty: function () {
+
+ return ( this.radius <= 0 );
+
+ },
+
+ containsPoint: function ( point ) {
+
+ return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) );
+
+ },
+
+ distanceToPoint: function ( point ) {
+
+ return ( point.distanceTo( this.center ) - this.radius );
+
+ },
+
+ intersectsSphere: function ( sphere ) {
+
+ var radiusSum = this.radius + sphere.radius;
+
+ return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum );
+
+ },
+
+ intersectsBox: function ( box ) {
+
+ return box.intersectsSphere( this );
+
+ },
+
+ intersectsPlane: function ( plane ) {
+
+ return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius;
+
+ },
+
+ clampPoint: function ( point, target ) {
+
+ var deltaLengthSq = this.center.distanceToSquared( point );
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Sphere: .clampPoint() target is now required' );
+ target = new Vector3();
+
+ }
+
+ target.copy( point );
+
+ if ( deltaLengthSq > ( this.radius * this.radius ) ) {
+
+ target.sub( this.center ).normalize();
+ target.multiplyScalar( this.radius ).add( this.center );
+
+ }
+
+ return target;
+
+ },
+
+ getBoundingBox: function ( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' );
+ target = new Box3();
+
+ }
+
+ target.set( this.center, this.center );
+ target.expandByScalar( this.radius );
+
+ return target;
+
+ },
+
+ applyMatrix4: function ( matrix ) {
+
+ this.center.applyMatrix4( matrix );
+ this.radius = this.radius * matrix.getMaxScaleOnAxis();
+
+ return this;
+
+ },
+
+ translate: function ( offset ) {
+
+ this.center.add( offset );
+
+ return this;
+
+ },
+
+ equals: function ( sphere ) {
+
+ return sphere.center.equals( this.center ) && ( sphere.radius === this.radius );
+
+ }
+
+ } );
+
+ /**
+ * @author bhouston / http://clara.io
+ */
+
+ function Plane( normal, constant ) {
+
+ // normal is assumed to be normalized
+
+ this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 );
+ this.constant = ( constant !== undefined ) ? constant : 0;
+
+ }
+
+ Object.assign( Plane.prototype, {
+
+ set: function ( normal, constant ) {
+
+ this.normal.copy( normal );
+ this.constant = constant;
+
+ return this;
+
+ },
+
+ setComponents: function ( x, y, z, w ) {
+
+ this.normal.set( x, y, z );
+ this.constant = w;
+
+ return this;
+
+ },
+
+ setFromNormalAndCoplanarPoint: function ( normal, point ) {
+
+ this.normal.copy( normal );
+ this.constant = - point.dot( this.normal );
+
+ return this;
+
+ },
+
+ setFromCoplanarPoints: function () {
+
+ var v1 = new Vector3();
+ var v2 = new Vector3();
+
+ return function setFromCoplanarPoints( a, b, c ) {
+
+ var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize();
+
+ // Q: should an error be thrown if normal is zero (e.g. degenerate plane)?
+
+ this.setFromNormalAndCoplanarPoint( normal, a );
+
+ return this;
+
+ };
+
+ }(),
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( plane ) {
+
+ this.normal.copy( plane.normal );
+ this.constant = plane.constant;
+
+ return this;
+
+ },
+
+ normalize: function () {
+
+ // Note: will lead to a divide by zero if the plane is invalid.
+
+ var inverseNormalLength = 1.0 / this.normal.length();
+ this.normal.multiplyScalar( inverseNormalLength );
+ this.constant *= inverseNormalLength;
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.constant *= - 1;
+ this.normal.negate();
+
+ return this;
+
+ },
+
+ distanceToPoint: function ( point ) {
+
+ return this.normal.dot( point ) + this.constant;
+
+ },
+
+ distanceToSphere: function ( sphere ) {
+
+ return this.distanceToPoint( sphere.center ) - sphere.radius;
+
+ },
+
+ projectPoint: function ( point, target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Plane: .projectPoint() target is now required' );
+ target = new Vector3();
+
+ }
+
+ return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point );
+
+ },
+
+ intersectLine: function () {
+
+ var v1 = new Vector3();
+
+ return function intersectLine( line, target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Plane: .intersectLine() target is now required' );
+ target = new Vector3();
+
+ }
+
+ var direction = line.delta( v1 );
+
+ var denominator = this.normal.dot( direction );
+
+ if ( denominator === 0 ) {
+
+ // line is coplanar, return origin
+ if ( this.distanceToPoint( line.start ) === 0 ) {
+
+ return target.copy( line.start );
+
+ }
+
+ // Unsure if this is the correct method to handle this case.
+ return undefined;
+
+ }
+
+ var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;
+
+ if ( t < 0 || t > 1 ) {
+
+ return undefined;
+
+ }
+
+ return target.copy( direction ).multiplyScalar( t ).add( line.start );
+
+ };
+
+ }(),
+
+ intersectsLine: function ( line ) {
+
+ // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.
+
+ var startSign = this.distanceToPoint( line.start );
+ var endSign = this.distanceToPoint( line.end );
+
+ return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );
+
+ },
+
+ intersectsBox: function ( box ) {
+
+ return box.intersectsPlane( this );
+
+ },
+
+ intersectsSphere: function ( sphere ) {
+
+ return sphere.intersectsPlane( this );
+
+ },
+
+ coplanarPoint: function ( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Plane: .coplanarPoint() target is now required' );
+ target = new Vector3();
+
+ }
+
+ return target.copy( this.normal ).multiplyScalar( - this.constant );
+
+ },
+
+ applyMatrix4: function () {
+
+ var v1 = new Vector3();
+ var m1 = new Matrix3();
+
+ return function applyMatrix4( matrix, optionalNormalMatrix ) {
+
+ var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix );
+
+ var referencePoint = this.coplanarPoint( v1 ).applyMatrix4( matrix );
+
+ var normal = this.normal.applyMatrix3( normalMatrix ).normalize();
+
+ this.constant = - referencePoint.dot( normal );
+
+ return this;
+
+ };
+
+ }(),
+
+ translate: function ( offset ) {
+
+ this.constant -= offset.dot( this.normal );
+
+ return this;
+
+ },
+
+ equals: function ( plane ) {
+
+ return plane.normal.equals( this.normal ) && ( plane.constant === this.constant );
+
+ }
+
+ } );
+
+ /**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author bhouston / http://clara.io
+ */
+
+ function Frustum( p0, p1, p2, p3, p4, p5 ) {
+
+ this.planes = [
+
+ ( p0 !== undefined ) ? p0 : new Plane(),
+ ( p1 !== undefined ) ? p1 : new Plane(),
+ ( p2 !== undefined ) ? p2 : new Plane(),
+ ( p3 !== undefined ) ? p3 : new Plane(),
+ ( p4 !== undefined ) ? p4 : new Plane(),
+ ( p5 !== undefined ) ? p5 : new Plane()
+
+ ];
+
+ }
+
+ Object.assign( Frustum.prototype, {
+
+ set: function ( p0, p1, p2, p3, p4, p5 ) {
+
+ var planes = this.planes;
+
+ planes[ 0 ].copy( p0 );
+ planes[ 1 ].copy( p1 );
+ planes[ 2 ].copy( p2 );
+ planes[ 3 ].copy( p3 );
+ planes[ 4 ].copy( p4 );
+ planes[ 5 ].copy( p5 );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( frustum ) {
+
+ var planes = this.planes;
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ planes[ i ].copy( frustum.planes[ i ] );
+
+ }
+
+ return this;
+
+ },
+
+ setFromMatrix: function ( m ) {
+
+ var planes = this.planes;
+ var me = m.elements;
+ var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];
+ var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];
+ var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];
+ var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];
+
+ planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();
+ planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();
+ planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();
+ planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();
+ planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();
+ planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize();
+
+ return this;
+
+ },
+
+ intersectsObject: function () {
+
+ var sphere = new Sphere();
+
+ return function intersectsObject( object ) {
+
+ var geometry = object.geometry;
+
+ if ( geometry.boundingSphere === null )
+ geometry.computeBoundingSphere();
+
+ sphere.copy( geometry.boundingSphere )
+ .applyMatrix4( object.matrixWorld );
+
+ return this.intersectsSphere( sphere );
+
+ };
+
+ }(),
+
+ intersectsSprite: function () {
+
+ var sphere = new Sphere();
+
+ return function intersectsSprite( sprite ) {
+
+ sphere.center.set( 0, 0, 0 );
+ sphere.radius = 0.7071067811865476;
+ sphere.applyMatrix4( sprite.matrixWorld );
+
+ return this.intersectsSphere( sphere );
+
+ };
+
+ }(),
+
+ intersectsSphere: function ( sphere ) {
+
+ var planes = this.planes;
+ var center = sphere.center;
+ var negRadius = - sphere.radius;
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ var distance = planes[ i ].distanceToPoint( center );
+
+ if ( distance < negRadius ) {
+
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+ },
+
+ intersectsBox: function () {
+
+ var p1 = new Vector3(),
+ p2 = new Vector3();
+
+ return function intersectsBox( box ) {
+
+ var planes = this.planes;
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ var plane = planes[ i ];
+
+ p1.x = plane.normal.x > 0 ? box.min.x : box.max.x;
+ p2.x = plane.normal.x > 0 ? box.max.x : box.min.x;
+ p1.y = plane.normal.y > 0 ? box.min.y : box.max.y;
+ p2.y = plane.normal.y > 0 ? box.max.y : box.min.y;
+ p1.z = plane.normal.z > 0 ? box.min.z : box.max.z;
+ p2.z = plane.normal.z > 0 ? box.max.z : box.min.z;
+
+ var d1 = plane.distanceToPoint( p1 );
+ var d2 = plane.distanceToPoint( p2 );
+
+ // if both outside plane, no intersection
+
+ if ( d1 < 0 && d2 < 0 ) {
+
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+ };
+
+ }(),
+
+ containsPoint: function ( point ) {
+
+ var planes = this.planes;
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ if ( planes[ i ].distanceToPoint( point ) < 0 ) {
+
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+ }
+
+ } );
+
+ var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif\n";
+
+ var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif\n";
+
+ var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif\n";
+
+ var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif\n";
+
+ var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif";
+
+ var begin_vertex = "\nvec3 transformed = vec3( position );\n";
+
+ var beginnormal_vertex = "\nvec3 objectNormal = vec3( normal );\n";
+
+ var bsdfs = "float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\tif( decayExponent > 0.0 ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\tfloat maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\treturn distanceFalloff * maxDistanceCutoffFactor;\n#else\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n#endif\n\t}\n\treturn 1.0;\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\tvec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\n\treturn specularColor * AB.x + AB.y;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n";
+
+ var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tfDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif\n";
+
+ var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\tif ( clipped ) discard;\n\t#endif\n#endif\n";
+
+ var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\t#if ! defined( PHYSICAL ) && ! defined( PHONG )\n\t\tvarying vec3 vViewPosition;\n\t#endif\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif\n";
+
+ var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvarying vec3 vViewPosition;\n#endif\n";
+
+ var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n";
+
+ var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif";
+
+ var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif\n";
+
+ var color_pars_vertex = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif";
+
+ var color_vertex = "#ifdef USE_COLOR\n\tvColor.xyz = color.xyz;\n#endif";
+
+ var common = "#define PI 3.14159265359\n#define PI2 6.28318530718\n#define PI_HALF 1.5707963267949\n#define RECIPROCAL_PI 0.31830988618\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\n";
+
+ var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n#define cubeUV_textureSize (1024.0)\nint getFaceFromDirection(vec3 direction) {\n\tvec3 absDirection = abs(direction);\n\tint face = -1;\n\tif( absDirection.x > absDirection.z ) {\n\t\tif(absDirection.x > absDirection.y )\n\t\t\tface = direction.x > 0.0 ? 0 : 3;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\telse {\n\t\tif(absDirection.z > absDirection.y )\n\t\t\tface = direction.z > 0.0 ? 2 : 5;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\treturn face;\n}\n#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\n\tfloat scale = exp2(cubeUV_maxLods1 - roughnessLevel);\n\tfloat dxRoughness = dFdx(roughness);\n\tfloat dyRoughness = dFdy(roughness);\n\tvec3 dx = dFdx( vec * scale * dxRoughness );\n\tvec3 dy = dFdy( vec * scale * dyRoughness );\n\tfloat d = max( dot( dx, dx ), dot( dy, dy ) );\n\td = clamp(d, 1.0, cubeUV_rangeClamp);\n\tfloat mipLevel = 0.5 * log2(d);\n\treturn vec2(floor(mipLevel), fract(mipLevel));\n}\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\n\tmipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\n\tfloat a = 16.0 * cubeUV_rcpTextureSize;\n\tvec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\n\tvec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\n\tfloat powScale = exp2_packed.x * exp2_packed.y;\n\tfloat scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\n\tfloat mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\n\tbool bRes = mipLevel == 0.0;\n\tscale = bRes && (scale < a) ? a : scale;\n\tvec3 r;\n\tvec2 offset;\n\tint face = getFaceFromDirection(direction);\n\tfloat rcpPowScale = 1.0 / powScale;\n\tif( face == 0) {\n\t\tr = vec3(direction.x, -direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 1) {\n\t\tr = vec3(direction.y, direction.x, direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 2) {\n\t\tr = vec3(direction.z, direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 3) {\n\t\tr = vec3(direction.x, direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse if( face == 4) {\n\t\tr = vec3(direction.y, direction.x, -direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse {\n\t\tr = vec3(direction.z, -direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\tr = normalize(r);\n\tfloat texelOffset = 0.5 * cubeUV_rcpTextureSize;\n\tvec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\n\tvec2 base = offset + vec2( texelOffset );\n\treturn base + s * ( scale - 2.0 * texelOffset );\n}\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\nvec4 textureCubeUV(vec3 reflectedDirection, float roughness ) {\n\tfloat roughnessVal = roughness* cubeUV_maxLods3;\n\tfloat r1 = floor(roughnessVal);\n\tfloat r2 = r1 + 1.0;\n\tfloat t = fract(roughnessVal);\n\tvec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\n\tfloat s = mipInfo.y;\n\tfloat level0 = mipInfo.x;\n\tfloat level1 = level0 + 1.0;\n\tlevel1 = level1 > 5.0 ? 5.0 : level1;\n\tlevel0 += min( floor( s + 0.5 ), 5.0 );\n\tvec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\n\tvec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\n\tvec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\n\tvec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\n\tvec4 result = mix(color10, color20, t);\n\treturn vec4(result.rgb, 1.0);\n}\n#endif\n";
+
+ var defaultnormal_vertex = "vec3 transformedNormal = normalMatrix * objectNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n";
+
+ var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif\n";
+
+ var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif\n";
+
+ var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif\n";
+
+ var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif\n";
+
+ var encodings_fragment = " gl_FragColor = linearToOutputTexel( gl_FragColor );\n";
+
+ var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.w );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.xyz * value.w * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = min( floor( D ) / 255.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = value.rgb * cLogLuvM;\n\tXp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract(Le);\n\tvResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM;\n\treturn vec4( max(vRGB, 0.0), 1.0 );\n}\n";
+
+ var envmap_fragment = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\tvec2 sampleUV;\n\t\treflectVec = normalize( reflectVec );\n\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\tvec4 envColor = texture2D( envMap, sampleUV );\n\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\treflectVec = normalize( reflectVec );\n\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\n\t\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\tenvColor = envMapTexelToLinear( envColor );\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif\n";
+
+ var envmap_pars_fragment = "#if defined( USE_ENVMAP ) || defined( PHYSICAL )\n\tuniform float reflectivity;\n\tuniform float envMapIntensity;\n#endif\n#ifdef USE_ENVMAP\n\t#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )\n\t\tvarying vec3 vWorldPosition;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif\n";
+
+ var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif\n";
+
+ var envmap_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif\n";
+
+ var fog_vertex = "\n#ifdef USE_FOG\nfogDepth = -mvPosition.z;\n#endif";
+
+ var fog_pars_vertex = "#ifdef USE_FOG\n varying float fogDepth;\n#endif\n";
+
+ var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 ) );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif\n";
+
+ var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif\n";
+
+ var gradientmap_pars_fragment = "#ifdef TOON\n\tuniform sampler2D gradientMap;\n\tvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\t\tfloat dotNL = dot( normal, lightDirection );\n\t\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t\t#ifdef USE_GRADIENTMAP\n\t\t\treturn texture2D( gradientMap, coord ).rgb;\n\t\t#else\n\t\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t\t#endif\n\t}\n#endif\n";
+
+ var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif\n";
+
+ var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif";
+
+ var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n#endif\n";
+
+ var lights_pars_begin = "uniform vec3 ambientLightColor;\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t\tfloat shadowCameraNear;\n\t\tfloat shadowCameraFar;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif\n";
+
+ var lights_pars_maps = "#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\tvec4 envMapColor = textureCubeUV( queryVec, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\tvec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent));\n\t\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\t\tvec2 sampleUV;\n\t\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif\n";
+
+ var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;\n";
+
+ var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3\tdiffuseColor;\n\tvec3\tspecularColor;\n\tfloat\tspecularShininess;\n\tfloat\tspecularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifdef TOON\n\t\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#else\n\t\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\t\tvec3 irradiance = dotNL * directLight.color;\n\t#endif\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)\n";
+
+ var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\n#ifdef STANDARD\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.clearCoat = saturate( clearCoat );\tmaterial.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );\n#endif\n";
+
+ var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3\tdiffuseColor;\n\tfloat\tspecularRoughness;\n\tvec3\tspecularColor;\n\t#ifndef STANDARD\n\t\tfloat clearCoat;\n\t\tfloat clearCoatRoughness;\n\t#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearCoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos - halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos + halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos + halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos - halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifndef STANDARD\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\n\treflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\t#ifndef STANDARD\n\t\treflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifndef STANDARD\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\tfloat dotNL = dotNV;\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\n\t#ifndef STANDARD\n\t\treflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\n#define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness )\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}\n";
+
+ var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = normalize( vViewPosition );\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearCoatRadiance = vec3( 0.0 );\n#endif\n";
+
+ var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tirradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), maxMipLevel );\n\t#ifndef STANDARD\n\t\tclearCoatRadiance += getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), maxMipLevel );\n\t#endif\n#endif\n";
+
+ var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight );\n#endif\n";
+
+ var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif";
+
+ var logdepthbuf_pars_fragment = "#ifdef USE_LOGDEPTHBUF\n\tuniform float logDepthBufFC;\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n#endif\n";
+
+ var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n\tuniform float logDepthBufFC;\n#endif";
+
+ var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t#else\n\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\tgl_Position.z *= gl_Position.w;\n\t#endif\n#endif\n";
+
+ var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif\n";
+
+ var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n";
+
+ var map_particle_fragment = "#ifdef USE_MAP\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n";
+
+ var map_particle_pars_fragment = "#ifdef USE_MAP\n\tuniform mat3 uvTransform;\n\tuniform sampler2D map;\n#endif\n";
+
+ var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif\n";
+
+ var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif";
+
+ var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tobjectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tobjectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tobjectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n#endif\n";
+
+ var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\t#ifndef USE_MORPHNORMALS\n\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif";
+
+ var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\ttransformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\ttransformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\ttransformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\ttransformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\ttransformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\ttransformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\ttransformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\t#endif\n#endif\n";
+
+ var normal_fragment_begin = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n#endif\n";
+
+ var normal_fragment_maps = "#ifdef USE_NORMALMAP\n\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n";
+
+ var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tfloat scale = sign( st1.t * st0.s - st0.t * st1.s );\n\t\tvec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );\n\t\tvec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );\n\t\tvec3 N = normalize( surf_norm );\n\t\tmat3 tsn = mat3( S, T, N );\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy *= normalScale;\n\t\tmapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif\n";
+
+ var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}\n";
+
+ var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif\n";
+
+ var project_vertex = "vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\ngl_Position = projectionMatrix * mvPosition;\n";
+
+ var dithering_fragment = "#if defined( DITHERING )\n gl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif\n";
+
+ var dithering_pars_fragment = "#if defined( DITHERING )\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif\n";
+
+ var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif\n";
+
+ var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif";
+
+ var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tfloat texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\n\t\tconst vec2 offset = vec2( 0.0, 1.0 );\n\t\tvec2 texelSize = vec2( 1.0 ) / size;\n\t\tvec2 centroidUV = floor( uv * size + 0.5 ) / size;\n\t\tfloat lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\n\t\tfloat lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\n\t\tfloat rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\n\t\tfloat rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\n\t\tvec2 f = fract( uv * size + 0.5 );\n\t\tfloat a = mix( lb, lt, f.y );\n\t\tfloat b = mix( rb, rt, f.y );\n\t\tfloat c = mix( a, b, f.x );\n\t\treturn c;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif\n";
+
+ var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n#endif\n";
+
+ var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n#endif\n";
+
+ var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\tDirectionalLight directionalLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tshadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\tSpotLight spotLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tshadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\tPointLight pointLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tshadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#endif\n\t#endif\n\treturn shadow;\n}\n";
+
+ var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif";
+
+ var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif\n";
+
+ var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif\n";
+
+ var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n#endif\n";
+
+ var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif";
+
+ var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif";
+
+ var tonemapping_fragment = "#if defined( TONE_MAPPING )\n gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif\n";
+
+ var tonemapping_pars_fragment = "#ifndef saturate\n\t#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\n";
+
+ var uv_pars_fragment = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n#endif";
+
+ var uv_pars_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n\tuniform mat3 uvTransform;\n#endif\n";
+
+ var uv_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif";
+
+ var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif";
+
+ var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n#endif";
+
+ var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = uv2;\n#endif";
+
+ var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n#endif\n";
+
+ var cube_frag = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldPosition;\nvoid main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n\tgl_FragColor.a *= opacity;\n}\n";
+
+ var cube_vert = "varying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}\n";
+
+ var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - gl_FragCoord.z ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\n\t#endif\n}\n";
+
+ var depth_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n";
+
+ var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}\n";
+
+ var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}\n";
+
+ var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldPosition );\n\tvec2 sampleUV;\n\tsampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n}\n";
+
+ var equirect_vert = "varying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}\n";
+
+ var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n";
+
+ var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvLineDistance = scale * lineDistance;\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}\n";
+
+ var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\treflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n";
+
+ var meshbasic_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n";
+
+ var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\treflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n";
+
+ var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n";
+
+ var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include