diff --git a/.idea/a4-creativecoding.iml b/.idea/a4-creativecoding.iml new file mode 100644 index 000000000..6f0659fcb --- /dev/null +++ b/.idea/a4-creativecoding.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 000000000..15a15b218 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..28a804d89 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..80cb98626 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..e5d5ba3a9 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 7f170cdb2..d142df15e 100755 --- a/README.md +++ b/README.md @@ -1,72 +1,40 @@ Assignment 4 - Creative Coding: Interactive Multimedia Experiences === -Due: September 27th, by 11:59 PM. +http://a4-heartkiller.herokuapp.com/ -For this assignment we will focus on client-side development using popular audio/graphics/visualization technologies; the server requirements are minimal. The goal of this assignment is to refine our JavaScript knowledge while exploring the multimedia capabilities of the browser. +**DAYDREAM — join your friends in a 3D World** -Baseline Requirements ---- - -Your application is required to implement the following functionalities: +Daydream is a simple 3D multi-character polygon world where each user is randomly assigned a unique character (policeman, robber, beach lady, fireman etc.) and gets to walk around in a town section and meet other users as other characters. An on-screen joystick is provided for motion; just simply drag the joystick in the direction you want your character to move. The joystick can make a character walk and run forward, backwards, and turn. Users can communicate with each other by moving their characters relatively close to each other's (**Multiple people need to visit the link to make it multi-character, if there's only one user, then you won't be able to trigger the chat function, you need another user for you to interact with**), and trigger the chatbox to type and send text by tapping toward the bottom center of the screen. The right place to tap to trigger the chatbox varies depending on where your character is standing from the other, but it can usually be in the bottom center. (The chatbox is **incredibly tricky and hard** to trigger, due to time constraints I was not able to figure out where exactly on the screen user needs to tap each time. But I have fortunately managed to tap in the right place multiple times to know that it actually works.) Your character will be present in the world until you close your tab; that automatically exits the app and removes your character from the town. -- A server created using Express (you can also use an alternative server framework such as Koa) for basic file delivery and middleware. Your middleware stack should include the `compression` and `helmet` [middlewares]((https://expressjs.com/en/resources/middleware.html)) by default. You are not required to use Glitch for this assignment (but using Glitch is fine!); [Heroku](https://www.heroku.com) is another excellent option to explore. The course staff can't be resposible for helping with all other hosting options outside of Glitch, but some of us do have experience with other systems. It also never hurts to ask on Slack, as there's 99 other classmates who might have the experience you're looking for! -- A client-side interactive experience using at least one of the web technologies frameworks we discussed in class over the past week. - - [Three.js](https://threejs.org/): A library for 3D graphics / VR experiences - - [D3.js](https://d3js.org): A library that is primarily used for interactive data visualizations - - [Canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API): A 2D raster drawing API included in all modern browsers - - [SVG](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API): A 2D vector drawing framework that enables shapes to be defined via XML. - - [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API): An API for audio synthesis, analysis, processing, and file playback. -- A user interface for interaction with your project, which must expose at least six parameters for user control. [dat.gui](https://workshop.chromeexperiments.com/examples/gui/#1--Basic-Usage) is highly recommended for this. You might also explore interaction by tracking mouse movement via the `window.onmousemove` event handler in tandem with the `event.clientX` and `event.clientY` properties. Consider using the [Pointer Events API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events) to ensure that that mouse and touch events will both be supported in your app. -- Your application should display basic documentation for the user interface when the application first loads. This documentation should be dismissable, however, users should be able to redisplay it via either a help buton (this could, for example, be inside a dat.gui interface) or via a keyboard shortcut (commonly the question mark). -- Your application should feature at least two different ES6 modules that you write ([read about ES6 modules](https://www.sitepoint.com/understanding-es6-modules/)) and include into a main JavaScript file. This means that you will need to author *at least three JavaScript files* (a `app.js` or `main.js` file and two modules). We'll discuss modules in class on Monday 9/23; for this assignment modules should contain at least two functions. -- You are required to use a linter for your JavaScript. There are plugins for most IDEs, however it will be difficult to run the linter directly in Glitch. If you haven't moved to developing on your personal laptop and then uploading to Glitch when your project is completed, this is the assignment to do so! -- Your HTML and CSS should validate. There are options/plugins for most IDEs to check validation. +**Obstacles** -The interactive experience should possess a reasonable level of complexity. Some examples: -### Three.js -- A generative algorithm creates simple agents that move through a virtual world. Your interface controls the behavior / appearance of these agents. -- A simple 3D game -- An 3D audio visualization of a song of your choosing. User interaction should control aspects of the visualization. -### Canvas -- Implement a generative algorithm such as [Conway's Game of Life](https://bitstorm.org/gameoflife/) (or 1D cellular automata) and provide interactive controls. Note that the Game of Life has been created by 100s of people using ; we'll be checking to ensure that your implementation is not a copy of these. -- Design a 2D audio visualizer of a song of your choosing. User interaction should control visual aspects of the experience. -### Web Audio API -- Create a screen-based musical instrument using the Web Audio API. You can use projects such as [Interface.js](http://charlie-roberts.com/interface/) or [Nexus UI](https://nexus-js.github.io/ui/api/#Piano) to provide common musical interface elements, or use dat.GUI in combination with mouse/touch events (use the Pointer Events API). Your GUI should enable users to control aspects of sound synthesis. -### D3.js -- Create visualizations using the datasets found at [Awesome JSON Datasets](https://github.com/jdorfman/Awesome-JSON-Datasets). Experiment with providing different visualizations of the same data set, and providing users interactive control over visualization parameters and/or data filtering. Alternatively, create a single visualization with using one of the more complicated techniques shown at [d3js.org](d3js.org) and provide meaningful points of interaction for users. +- **Hanging Chatboxes**: After a character exits the world, their speech bubble remains in the air. +- **Buggy if 3+ users/characters**: I've noticed that as soon as a third character joins the world and the other two starts a conversation, one of those two disappears from the 3rd character's view, even though they still exist in the views of each other. +- **Missing texture**: Occasionally when a user enters the world, their character might not have any texture applied to them and this leads the character to just appear as dark blank cubes. +- **Missing Stylesheet**: Often times, the top banner would appear plain and unstyled and inspecting the console.log would indicate that Heroku is not able to load the Stylesheet properly. This problem did not occur when I was deploying locally. +- **Blank Modal**: I tried to use the Bootstrap modal to inform users with general info about the game that pops up at first page load and can be brought back by a button at the top corner, but the modal would pop up only without the text inside. I was not able to fix it on time and hence, I resorted to a less fancy way of displaying info inside a banner-like element that stays at the top of the screen at all-times. +- **Deploying**: This was actually the most challenging part for me since I was not able to host on Glitch after trying for a lot of hours, I resorted to deploying via Heroku, which also imposed a lot of new challenges and took a lot of time. +- **Works better locally**: The app functions way better when I was implementing and hosting it locally on my laptop via Webstorm. The chat feature is way smoother, and the top banner is always styled properly via css. I believe some things got lost in translation once I uploaded the project to places such as Glitch, Goorm, Heroku etc. (for example, unecessary file paths, conflicting port numbers etc.) -Deliverables ---- +**Linter** -Do the following to complete this assignment: +I coded this project primarily in WebStorm and therefore applied the default linter. -1. Implement your project with the above requirements. -3. Test your project to make sure that when someone goes to your main page on Glitch/Heroku/etc., it displays correctly. -4. Ensure that your project has the proper naming scheme `a4-yourname` so we can find it. -5. Fork this repository and modify the README to the specifications below. *NOTE: If you don't use Glitch for hosting (where we can see the files) then you must include all project files that you author in your repo for this assignment*. -6. Create and submit a Pull Request to the original repo. Name the pull request using the following template: `a4-gitname-firstname-lastname`. - -Sample Readme (delete the above when you're ready to submit, and modify the below so with your links and descriptions) --- -## Your Web Application Title - -your hosting link e.g. http://a4-charlieroberts.glitch.me - -Include a very brief summary of your project here. Images are encouraged, along with concise, high-level text. Be sure to include: - -- the goal of the application -- challenges you faced in realizing the application -- a brief description of the JS linter you used and what rules it follows (we'll be looking at your JS files for consistency) - ## Technical Achievements -- **Tech Achievement 1**: I wrote my own custom GLSL shaders to use as a material for my Three.js objects. -- **Tech Achievement 2**: My audiovisualizer uses both FFT and amplitude analysis to drive visualization. -- **Tech Achievement 3**: I optimized the efficiency of my reaction-diffusion algorithm by... -- **Tech Achievement 4**: I visualized the dataset X using three different visualization technqiues provided by D3, andprovided +- **Three.js**: I used and rendered the pre-made Three.js 3D models via Three.js. The Raycasting property of Three.js was used to apply most of the app logic such as blocking, moving up/down through unleveled ground etc. +- **Socket.io**: Socket.io was mainly used to communicate bewteen client and server. This primarily gave birth to the multi-user aspect of the app and the chat feature. +- **FBXLoader**: To load the fbx files (3D models) into the code. +- **Mixamo**: I used mixamo.com to apply animations to custom characters. +- **Autodesk Maya**: I used Autodesk Maya to combine the 3D models (fbx files) into one and export, for instance, creating a town model by putting models of items such as cars, buidlings, signs, and lamp posts inside accordingly. +- **Blender**: I used blender to tweak the 3D models such as texture, shape, skin, color, etc. +- **Heroku**: Since Glitch gave me a myriad of problems, I had to resort to Goorm IDE, which in turn gave me a lot more problems, but it ulitmately helped deploy the app via Heroku. ### Design/Evaluation Achievements -- **Design Achievement 1**: I ensured that my application would run on both desktops / mobile devices by changing X -- **Design Achievement 2**: I followed best practices for accessibility, including providing alt attributes for images and using semantic HTML. There are no `
` or `` elements in my document. -- **Design Achievement 3**: We tested the application with n=X users, finding that... + +I focused on the technical aspects of the assignment but still managed to do some styling. +- **Bootstrap**: I used Bootstrap to style the top banner, however, sometimes Heroku is not able to find the stylesheet and the button stays unstyled. +- **Responsive**: The app is accessible via mobile as well, although the chat feature might not work as expected. ALthough, PCs and laptops are recommended for full experience. + diff --git a/daydream/node_modules/.bin/mime b/daydream/node_modules/.bin/mime new file mode 100644 index 000000000..fbb7ee0ee --- /dev/null +++ b/daydream/node_modules/.bin/mime @@ -0,0 +1 @@ +../mime/cli.js \ No newline at end of file diff --git a/daydream/node_modules/accepts/HISTORY.md b/daydream/node_modules/accepts/HISTORY.md new file mode 100644 index 000000000..f16c17a56 --- /dev/null +++ b/daydream/node_modules/accepts/HISTORY.md @@ -0,0 +1,224 @@ +1.3.5 / 2018-02-28 +================== + + * deps: mime-types@~2.1.18 + - deps: mime-db@~1.33.0 + +1.3.4 / 2017-08-22 +================== + + * deps: mime-types@~2.1.16 + - deps: mime-db@~1.29.0 + +1.3.3 / 2016-05-02 +================== + + * deps: mime-types@~2.1.11 + - deps: mime-db@~1.23.0 + * deps: negotiator@0.6.1 + - perf: improve `Accept` parsing speed + - perf: improve `Accept-Charset` parsing speed + - perf: improve `Accept-Encoding` parsing speed + - perf: improve `Accept-Language` parsing speed + +1.3.2 / 2016-03-08 +================== + + * deps: mime-types@~2.1.10 + - Fix extension of `application/dash+xml` + - Update primary extension for `audio/mp4` + - deps: mime-db@~1.22.0 + +1.3.1 / 2016-01-19 +================== + + * deps: mime-types@~2.1.9 + - deps: mime-db@~1.21.0 + +1.3.0 / 2015-09-29 +================== + + * deps: mime-types@~2.1.7 + - deps: mime-db@~1.19.0 + * deps: negotiator@0.6.0 + - Fix including type extensions in parameters in `Accept` parsing + - Fix parsing `Accept` parameters with quoted equals + - Fix parsing `Accept` parameters with quoted semicolons + - Lazy-load modules from main entry point + - perf: delay type concatenation until needed + - perf: enable strict mode + - perf: hoist regular expressions + - perf: remove closures getting spec properties + - perf: remove a closure from media type parsing + - perf: remove property delete from media type parsing + +1.2.13 / 2015-09-06 +=================== + + * deps: mime-types@~2.1.6 + - deps: mime-db@~1.18.0 + +1.2.12 / 2015-07-30 +=================== + + * deps: mime-types@~2.1.4 + - deps: mime-db@~1.16.0 + +1.2.11 / 2015-07-16 +=================== + + * deps: mime-types@~2.1.3 + - deps: mime-db@~1.15.0 + +1.2.10 / 2015-07-01 +=================== + + * deps: mime-types@~2.1.2 + - deps: mime-db@~1.14.0 + +1.2.9 / 2015-06-08 +================== + + * deps: mime-types@~2.1.1 + - perf: fix deopt during mapping + +1.2.8 / 2015-06-07 +================== + + * deps: mime-types@~2.1.0 + - deps: mime-db@~1.13.0 + * perf: avoid argument reassignment & argument slice + * perf: avoid negotiator recursive construction + * perf: enable strict mode + * perf: remove unnecessary bitwise operator + +1.2.7 / 2015-05-10 +================== + + * deps: negotiator@0.5.3 + - Fix media type parameter matching to be case-insensitive + +1.2.6 / 2015-05-07 +================== + + * deps: mime-types@~2.0.11 + - deps: mime-db@~1.9.1 + * deps: negotiator@0.5.2 + - Fix comparing media types with quoted values + - Fix splitting media types with quoted commas + +1.2.5 / 2015-03-13 +================== + + * deps: mime-types@~2.0.10 + - deps: mime-db@~1.8.0 + +1.2.4 / 2015-02-14 +================== + + * Support Node.js 0.6 + * deps: mime-types@~2.0.9 + - deps: mime-db@~1.7.0 + * deps: negotiator@0.5.1 + - Fix preference sorting to be stable for long acceptable lists + +1.2.3 / 2015-01-31 +================== + + * deps: mime-types@~2.0.8 + - deps: mime-db@~1.6.0 + +1.2.2 / 2014-12-30 +================== + + * deps: mime-types@~2.0.7 + - deps: mime-db@~1.5.0 + +1.2.1 / 2014-12-30 +================== + + * deps: mime-types@~2.0.5 + - deps: mime-db@~1.3.1 + +1.2.0 / 2014-12-19 +================== + + * deps: negotiator@0.5.0 + - Fix list return order when large accepted list + - Fix missing identity encoding when q=0 exists + - Remove dynamic building of Negotiator class + +1.1.4 / 2014-12-10 +================== + + * deps: mime-types@~2.0.4 + - deps: mime-db@~1.3.0 + +1.1.3 / 2014-11-09 +================== + + * deps: mime-types@~2.0.3 + - deps: mime-db@~1.2.0 + +1.1.2 / 2014-10-14 +================== + + * deps: negotiator@0.4.9 + - Fix error when media type has invalid parameter + +1.1.1 / 2014-09-28 +================== + + * deps: mime-types@~2.0.2 + - deps: mime-db@~1.1.0 + * deps: negotiator@0.4.8 + - Fix all negotiations to be case-insensitive + - Stable sort preferences of same quality according to client order + +1.1.0 / 2014-09-02 +================== + + * update `mime-types` + +1.0.7 / 2014-07-04 +================== + + * Fix wrong type returned from `type` when match after unknown extension + +1.0.6 / 2014-06-24 +================== + + * deps: negotiator@0.4.7 + +1.0.5 / 2014-06-20 +================== + + * fix crash when unknown extension given + +1.0.4 / 2014-06-19 +================== + + * use `mime-types` + +1.0.3 / 2014-06-11 +================== + + * deps: negotiator@0.4.6 + - Order by specificity when quality is the same + +1.0.2 / 2014-05-29 +================== + + * Fix interpretation when header not in request + * deps: pin negotiator@0.4.5 + +1.0.1 / 2014-01-18 +================== + + * Identity encoding isn't always acceptable + * deps: negotiator@~0.4.0 + +1.0.0 / 2013-12-27 +================== + + * Genesis diff --git a/daydream/node_modules/accepts/LICENSE b/daydream/node_modules/accepts/LICENSE new file mode 100644 index 000000000..06166077b --- /dev/null +++ b/daydream/node_modules/accepts/LICENSE @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2015 Douglas Christopher Wilson + +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. diff --git a/daydream/node_modules/accepts/README.md b/daydream/node_modules/accepts/README.md new file mode 100644 index 000000000..6a2749a1a --- /dev/null +++ b/daydream/node_modules/accepts/README.md @@ -0,0 +1,143 @@ +# accepts + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Node.js Version][node-version-image]][node-version-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +Higher level content negotiation based on [negotiator](https://www.npmjs.com/package/negotiator). +Extracted from [koa](https://www.npmjs.com/package/koa) for general use. + +In addition to negotiator, it allows: + +- Allows types as an array or arguments list, ie `(['text/html', 'application/json'])` + as well as `('text/html', 'application/json')`. +- Allows type shorthands such as `json`. +- Returns `false` when no types match +- Treats non-existent headers as `*` + +## Installation + +This is a [Node.js](https://nodejs.org/en/) module available through the +[npm registry](https://www.npmjs.com/). Installation is done using the +[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): + +```sh +$ npm install accepts +``` + +## API + + + +```js +var accepts = require('accepts') +``` + +### accepts(req) + +Create a new `Accepts` object for the given `req`. + +#### .charset(charsets) + +Return the first accepted charset. If nothing in `charsets` is accepted, +then `false` is returned. + +#### .charsets() + +Return the charsets that the request accepts, in the order of the client's +preference (most preferred first). + +#### .encoding(encodings) + +Return the first accepted encoding. If nothing in `encodings` is accepted, +then `false` is returned. + +#### .encodings() + +Return the encodings that the request accepts, in the order of the client's +preference (most preferred first). + +#### .language(languages) + +Return the first accepted language. If nothing in `languages` is accepted, +then `false` is returned. + +#### .languages() + +Return the languages that the request accepts, in the order of the client's +preference (most preferred first). + +#### .type(types) + +Return the first accepted type (and it is returned as the same text as what +appears in the `types` array). If nothing in `types` is accepted, then `false` +is returned. + +The `types` array can contain full MIME types or file extensions. Any value +that is not a full MIME types is passed to `require('mime-types').lookup`. + +#### .types() + +Return the types that the request accepts, in the order of the client's +preference (most preferred first). + +## Examples + +### Simple type negotiation + +This simple example shows how to use `accepts` to return a different typed +respond body based on what the client wants to accept. The server lists it's +preferences in order and will get back the best match between the client and +server. + +```js +var accepts = require('accepts') +var http = require('http') + +function app (req, res) { + var accept = accepts(req) + + // the order of this list is significant; should be server preferred order + switch (accept.type(['json', 'html'])) { + case 'json': + res.setHeader('Content-Type', 'application/json') + res.write('{"hello":"world!"}') + break + case 'html': + res.setHeader('Content-Type', 'text/html') + res.write('hello, world!') + break + default: + // the fallback is text/plain, so no need to specify it above + res.setHeader('Content-Type', 'text/plain') + res.write('hello, world!') + break + } + + res.end() +} + +http.createServer(app).listen(3000) +``` + +You can test this out with the cURL program: +```sh +curl -I -H'Accept: text/html' http://localhost:3000/ +``` + +## License + +[MIT](LICENSE) + +[npm-image]: https://img.shields.io/npm/v/accepts.svg +[npm-url]: https://npmjs.org/package/accepts +[node-version-image]: https://img.shields.io/node/v/accepts.svg +[node-version-url]: https://nodejs.org/en/download/ +[travis-image]: https://img.shields.io/travis/jshttp/accepts/master.svg +[travis-url]: https://travis-ci.org/jshttp/accepts +[coveralls-image]: https://img.shields.io/coveralls/jshttp/accepts/master.svg +[coveralls-url]: https://coveralls.io/r/jshttp/accepts +[downloads-image]: https://img.shields.io/npm/dm/accepts.svg +[downloads-url]: https://npmjs.org/package/accepts diff --git a/daydream/node_modules/accepts/index.js b/daydream/node_modules/accepts/index.js new file mode 100644 index 000000000..e9b2f63fb --- /dev/null +++ b/daydream/node_modules/accepts/index.js @@ -0,0 +1,238 @@ +/*! + * accepts + * Copyright(c) 2014 Jonathan Ong + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module dependencies. + * @private + */ + +var Negotiator = require('negotiator') +var mime = require('mime-types') + +/** + * Module exports. + * @public + */ + +module.exports = Accepts + +/** + * Create a new Accepts object for the given req. + * + * @param {object} req + * @public + */ + +function Accepts (req) { + if (!(this instanceof Accepts)) { + return new Accepts(req) + } + + this.headers = req.headers + this.negotiator = new Negotiator(req) +} + +/** + * Check if the given `type(s)` is acceptable, returning + * the best match when true, otherwise `undefined`, in which + * case you should respond with 406 "Not Acceptable". + * + * The `type` value may be a single mime type string + * such as "application/json", the extension name + * such as "json" or an array `["json", "html", "text/plain"]`. When a list + * or array is given the _best_ match, if any is returned. + * + * Examples: + * + * // Accept: text/html + * this.types('html'); + * // => "html" + * + * // Accept: text/*, application/json + * this.types('html'); + * // => "html" + * this.types('text/html'); + * // => "text/html" + * this.types('json', 'text'); + * // => "json" + * this.types('application/json'); + * // => "application/json" + * + * // Accept: text/*, application/json + * this.types('image/png'); + * this.types('png'); + * // => undefined + * + * // Accept: text/*;q=.5, application/json + * this.types(['html', 'json']); + * this.types('html', 'json'); + * // => "json" + * + * @param {String|Array} types... + * @return {String|Array|Boolean} + * @public + */ + +Accepts.prototype.type = +Accepts.prototype.types = function (types_) { + var types = types_ + + // support flattened arguments + if (types && !Array.isArray(types)) { + types = new Array(arguments.length) + for (var i = 0; i < types.length; i++) { + types[i] = arguments[i] + } + } + + // no types, return all requested types + if (!types || types.length === 0) { + return this.negotiator.mediaTypes() + } + + // no accept header, return first given type + if (!this.headers.accept) { + return types[0] + } + + var mimes = types.map(extToMime) + var accepts = this.negotiator.mediaTypes(mimes.filter(validMime)) + var first = accepts[0] + + return first + ? types[mimes.indexOf(first)] + : false +} + +/** + * Return accepted encodings or best fit based on `encodings`. + * + * Given `Accept-Encoding: gzip, deflate` + * an array sorted by quality is returned: + * + * ['gzip', 'deflate'] + * + * @param {String|Array} encodings... + * @return {String|Array} + * @public + */ + +Accepts.prototype.encoding = +Accepts.prototype.encodings = function (encodings_) { + var encodings = encodings_ + + // support flattened arguments + if (encodings && !Array.isArray(encodings)) { + encodings = new Array(arguments.length) + for (var i = 0; i < encodings.length; i++) { + encodings[i] = arguments[i] + } + } + + // no encodings, return all requested encodings + if (!encodings || encodings.length === 0) { + return this.negotiator.encodings() + } + + return this.negotiator.encodings(encodings)[0] || false +} + +/** + * Return accepted charsets or best fit based on `charsets`. + * + * Given `Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5` + * an array sorted by quality is returned: + * + * ['utf-8', 'utf-7', 'iso-8859-1'] + * + * @param {String|Array} charsets... + * @return {String|Array} + * @public + */ + +Accepts.prototype.charset = +Accepts.prototype.charsets = function (charsets_) { + var charsets = charsets_ + + // support flattened arguments + if (charsets && !Array.isArray(charsets)) { + charsets = new Array(arguments.length) + for (var i = 0; i < charsets.length; i++) { + charsets[i] = arguments[i] + } + } + + // no charsets, return all requested charsets + if (!charsets || charsets.length === 0) { + return this.negotiator.charsets() + } + + return this.negotiator.charsets(charsets)[0] || false +} + +/** + * Return accepted languages or best fit based on `langs`. + * + * Given `Accept-Language: en;q=0.8, es, pt` + * an array sorted by quality is returned: + * + * ['es', 'pt', 'en'] + * + * @param {String|Array} langs... + * @return {Array|String} + * @public + */ + +Accepts.prototype.lang = +Accepts.prototype.langs = +Accepts.prototype.language = +Accepts.prototype.languages = function (languages_) { + var languages = languages_ + + // support flattened arguments + if (languages && !Array.isArray(languages)) { + languages = new Array(arguments.length) + for (var i = 0; i < languages.length; i++) { + languages[i] = arguments[i] + } + } + + // no languages, return all requested languages + if (!languages || languages.length === 0) { + return this.negotiator.languages() + } + + return this.negotiator.languages(languages)[0] || false +} + +/** + * Convert extnames to mime. + * + * @param {String} type + * @return {String} + * @private + */ + +function extToMime (type) { + return type.indexOf('/') === -1 + ? mime.lookup(type) + : type +} + +/** + * Check if mime is valid. + * + * @param {String} type + * @return {String} + * @private + */ + +function validMime (type) { + return typeof type === 'string' +} diff --git a/daydream/node_modules/accepts/package.json b/daydream/node_modules/accepts/package.json new file mode 100644 index 000000000..6e01a426d --- /dev/null +++ b/daydream/node_modules/accepts/package.json @@ -0,0 +1,86 @@ +{ + "_from": "accepts@~1.3.5", + "_id": "accepts@1.3.5", + "_inBundle": false, + "_integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "_location": "/accepts", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "accepts@~1.3.5", + "name": "accepts", + "escapedName": "accepts", + "rawSpec": "~1.3.5", + "saveSpec": null, + "fetchSpec": "~1.3.5" + }, + "_requiredBy": [ + "/engine.io", + "/express" + ], + "_resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "_shasum": "eb777df6011723a3b14e8a72c0805c8e86746bd2", + "_spec": "accepts@~1.3.5", + "_where": "/Users/nik/Documents/niksgames.com/nodeapps/udemy/blockland/node_modules/express", + "bugs": { + "url": "https://github.com/jshttp/accepts/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + } + ], + "dependencies": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + }, + "deprecated": false, + "description": "Higher-level content negotiation", + "devDependencies": { + "eslint": "4.18.1", + "eslint-config-standard": "11.0.0", + "eslint-plugin-import": "2.9.0", + "eslint-plugin-markdown": "1.0.0-beta.6", + "eslint-plugin-node": "6.0.1", + "eslint-plugin-promise": "3.6.0", + "eslint-plugin-standard": "3.0.1", + "istanbul": "0.4.5", + "mocha": "~1.21.5" + }, + "engines": { + "node": ">= 0.6" + }, + "files": [ + "LICENSE", + "HISTORY.md", + "index.js" + ], + "homepage": "https://github.com/jshttp/accepts#readme", + "keywords": [ + "content", + "negotiation", + "accept", + "accepts" + ], + "license": "MIT", + "name": "accepts", + "repository": { + "type": "git", + "url": "git+https://github.com/jshttp/accepts.git" + }, + "scripts": { + "lint": "eslint --plugin markdown --ext js,md .", + "test": "mocha --reporter spec --check-leaks --bail test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "version": "1.3.5" +} diff --git a/daydream/node_modules/after/.npmignore b/daydream/node_modules/after/.npmignore new file mode 100644 index 000000000..6c7860241 --- /dev/null +++ b/daydream/node_modules/after/.npmignore @@ -0,0 +1,2 @@ +node_modules +.monitor diff --git a/daydream/node_modules/after/.travis.yml b/daydream/node_modules/after/.travis.yml new file mode 100644 index 000000000..afd72d0e5 --- /dev/null +++ b/daydream/node_modules/after/.travis.yml @@ -0,0 +1,12 @@ +language: node_js +node_js: + - 0.6 + - 0.8 + - 0.9 + - 0.10 + - 0.12 + - 4.2.4 + - 5.4.1 + - iojs-1 + - iojs-2 + - iojs-3 diff --git a/daydream/node_modules/after/LICENCE b/daydream/node_modules/after/LICENCE new file mode 100644 index 000000000..7c3513068 --- /dev/null +++ b/daydream/node_modules/after/LICENCE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Raynos. + +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. \ No newline at end of file diff --git a/daydream/node_modules/after/README.md b/daydream/node_modules/after/README.md new file mode 100644 index 000000000..fc6909647 --- /dev/null +++ b/daydream/node_modules/after/README.md @@ -0,0 +1,115 @@ +# After [![Build Status][1]][2] + +Invoke callback after n calls + +## Status: production ready + +## Example + +```js +var after = require("after") +var db = require("./db") // some db. + +var updateUser = function (req, res) { + // use after to run two tasks in parallel, + // namely get request body and get session + // then run updateUser with the results + var next = after(2, updateUser) + var results = {} + + getJSONBody(req, res, function (err, body) { + if (err) return next(err) + + results.body = body + next(null, results) + }) + + getSessionUser(req, res, function (err, user) { + if (err) return next(err) + + results.user = user + next(null, results) + }) + + // now do the thing! + function updateUser(err, result) { + if (err) { + res.statusCode = 500 + return res.end("Unexpected Error") + } + + if (!result.user || result.user.role !== "admin") { + res.statusCode = 403 + return res.end("Permission Denied") + } + + db.put("users:" + req.params.userId, result.body, function (err) { + if (err) { + res.statusCode = 500 + return res.end("Unexpected Error") + } + + res.statusCode = 200 + res.end("Ok") + }) + } +} +``` + +## Naive Example + +```js +var after = require("after") + , next = after(3, logItWorks) + +next() +next() +next() // it works + +function logItWorks() { + console.log("it works!") +} +``` + +## Example with error handling + +```js +var after = require("after") + , next = after(3, logError) + +next() +next(new Error("oops")) // logs oops +next() // does nothing + +// This callback is only called once. +// If there is an error the callback gets called immediately +// this avoids the situation where errors get lost. +function logError(err) { + console.log(err) +} +``` + +## Installation + +`npm install after` + +## Tests + +`npm test` + +## Contributors + + - Raynos + - defunctzombie + +## MIT Licenced + + [1]: https://secure.travis-ci.org/Raynos/after.png + [2]: http://travis-ci.org/Raynos/after + [3]: http://raynos.org/blog/2/Flow-control-in-node.js + [4]: http://stackoverflow.com/questions/6852059/determining-the-end-of-asynchronous-operations-javascript/6852307#6852307 + [5]: http://stackoverflow.com/questions/6869872/in-javascript-what-are-best-practices-for-executing-multiple-asynchronous-functi/6870031#6870031 + [6]: http://stackoverflow.com/questions/6864397/javascript-performance-long-running-tasks/6889419#6889419 + [7]: http://stackoverflow.com/questions/6597493/synchronous-database-queries-with-node-js/6620091#6620091 + [8]: http://github.com/Raynos/iterators + [9]: http://github.com/Raynos/composite diff --git a/daydream/node_modules/after/index.js b/daydream/node_modules/after/index.js new file mode 100644 index 000000000..ec2487974 --- /dev/null +++ b/daydream/node_modules/after/index.js @@ -0,0 +1,28 @@ +module.exports = after + +function after(count, callback, err_cb) { + var bail = false + err_cb = err_cb || noop + proxy.count = count + + return (count === 0) ? callback() : proxy + + function proxy(err, result) { + if (proxy.count <= 0) { + throw new Error('after called too many times') + } + --proxy.count + + // after first error, rest are passed to err_cb + if (err) { + bail = true + callback(err) + // future error callbacks will go to error handler + callback = err_cb + } else if (proxy.count === 0 && !bail) { + callback(null, result) + } + } +} + +function noop() {} diff --git a/daydream/node_modules/after/package.json b/daydream/node_modules/after/package.json new file mode 100644 index 000000000..75d304ded --- /dev/null +++ b/daydream/node_modules/after/package.json @@ -0,0 +1,63 @@ +{ + "_from": "after@0.8.2", + "_id": "after@0.8.2", + "_inBundle": false, + "_integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "_location": "/after", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "after@0.8.2", + "name": "after", + "escapedName": "after", + "rawSpec": "0.8.2", + "saveSpec": null, + "fetchSpec": "0.8.2" + }, + "_requiredBy": [ + "/engine.io-parser" + ], + "_resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "_shasum": "fedb394f9f0e02aa9768e702bda23b505fae7e1f", + "_spec": "after@0.8.2", + "_where": "/Users/nik/Documents/niksgames.com/nodeapps/udemy/blockland/node_modules/engine.io-parser", + "author": { + "name": "Raynos", + "email": "raynos2@gmail.com" + }, + "bugs": { + "url": "https://github.com/Raynos/after/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Raynos", + "email": "raynos2@gmail.com", + "url": "http://raynos.org" + } + ], + "deprecated": false, + "description": "after - tiny flow control", + "devDependencies": { + "mocha": "~1.8.1" + }, + "homepage": "https://github.com/Raynos/after#readme", + "keywords": [ + "flowcontrol", + "after", + "flow", + "control", + "arch" + ], + "license": "MIT", + "name": "after", + "repository": { + "type": "git", + "url": "git://github.com/Raynos/after.git" + }, + "scripts": { + "test": "mocha --ui tdd --reporter spec test/*.js" + }, + "version": "0.8.2" +} diff --git a/daydream/node_modules/after/test/after-test.js b/daydream/node_modules/after/test/after-test.js new file mode 100644 index 000000000..0d63f4c24 --- /dev/null +++ b/daydream/node_modules/after/test/after-test.js @@ -0,0 +1,120 @@ +/*global suite, test*/ + +var assert = require("assert") + , after = require("../") + +test("exists", function () { + assert(typeof after === "function", "after is not a function") +}) + +test("after when called with 0 invokes", function (done) { + after(0, done) +}); + +test("after 1", function (done) { + var next = after(1, done) + next() +}) + +test("after 5", function (done) { + var next = after(5, done) + , i = 5 + + while (i--) { + next() + } +}) + +test("manipulate count", function (done) { + var next = after(1, done) + , i = 5 + + next.count = i + while (i--) { + next() + } +}) + +test("after terminates on error", function (done) { + var next = after(2, function(err) { + assert.equal(err.message, 'test'); + done(); + }) + next(new Error('test')) + next(new Error('test2')) +}) + +test('gee', function(done) { + done = after(2, done) + + function cb(err) { + assert.equal(err.message, 1); + done() + } + + var next = after(3, cb, function(err) { + assert.equal(err.message, 2) + done() + }); + + next() + next(new Error(1)) + next(new Error(2)) +}) + +test('eee', function(done) { + done = after(3, done) + + function cb(err) { + assert.equal(err.message, 1); + done() + } + + var next = after(3, cb, function(err) { + assert.equal(err.message, 2) + done() + }); + + next(new Error(1)) + next(new Error(2)) + next(new Error(2)) +}) + +test('gge', function(done) { + function cb(err) { + assert.equal(err.message, 1); + done() + } + + var next = after(3, cb, function(err) { + // should not happen + assert.ok(false); + }); + + next() + next() + next(new Error(1)) +}) + +test('egg', function(done) { + function cb(err) { + assert.equal(err.message, 1); + done() + } + + var next = after(3, cb, function(err) { + // should not happen + assert.ok(false); + }); + + next(new Error(1)) + next() + next() +}) + +test('throws on too many calls', function(done) { + var next = after(1, done); + next() + assert.throws(next, /after called too many times/); +}); + diff --git a/daydream/node_modules/array-flatten/LICENSE b/daydream/node_modules/array-flatten/LICENSE new file mode 100644 index 000000000..983fbe8ae --- /dev/null +++ b/daydream/node_modules/array-flatten/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Blake Embrey (hello@blakeembrey.com) + +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. diff --git a/daydream/node_modules/array-flatten/README.md b/daydream/node_modules/array-flatten/README.md new file mode 100644 index 000000000..91fa5b637 --- /dev/null +++ b/daydream/node_modules/array-flatten/README.md @@ -0,0 +1,43 @@ +# Array Flatten + +[![NPM version][npm-image]][npm-url] +[![NPM downloads][downloads-image]][downloads-url] +[![Build status][travis-image]][travis-url] +[![Test coverage][coveralls-image]][coveralls-url] + +> Flatten an array of nested arrays into a single flat array. Accepts an optional depth. + +## Installation + +``` +npm install array-flatten --save +``` + +## Usage + +```javascript +var flatten = require('array-flatten') + +flatten([1, [2, [3, [4, [5], 6], 7], 8], 9]) +//=> [1, 2, 3, 4, 5, 6, 7, 8, 9] + +flatten([1, [2, [3, [4, [5], 6], 7], 8], 9], 2) +//=> [1, 2, 3, [4, [5], 6], 7, 8, 9] + +(function () { + flatten(arguments) //=> [1, 2, 3] +})(1, [2, 3]) +``` + +## License + +MIT + +[npm-image]: https://img.shields.io/npm/v/array-flatten.svg?style=flat +[npm-url]: https://npmjs.org/package/array-flatten +[downloads-image]: https://img.shields.io/npm/dm/array-flatten.svg?style=flat +[downloads-url]: https://npmjs.org/package/array-flatten +[travis-image]: https://img.shields.io/travis/blakeembrey/array-flatten.svg?style=flat +[travis-url]: https://travis-ci.org/blakeembrey/array-flatten +[coveralls-image]: https://img.shields.io/coveralls/blakeembrey/array-flatten.svg?style=flat +[coveralls-url]: https://coveralls.io/r/blakeembrey/array-flatten?branch=master diff --git a/daydream/node_modules/array-flatten/array-flatten.js b/daydream/node_modules/array-flatten/array-flatten.js new file mode 100644 index 000000000..089117b32 --- /dev/null +++ b/daydream/node_modules/array-flatten/array-flatten.js @@ -0,0 +1,64 @@ +'use strict' + +/** + * Expose `arrayFlatten`. + */ +module.exports = arrayFlatten + +/** + * Recursive flatten function with depth. + * + * @param {Array} array + * @param {Array} result + * @param {Number} depth + * @return {Array} + */ +function flattenWithDepth (array, result, depth) { + for (var i = 0; i < array.length; i++) { + var value = array[i] + + if (depth > 0 && Array.isArray(value)) { + flattenWithDepth(value, result, depth - 1) + } else { + result.push(value) + } + } + + return result +} + +/** + * Recursive flatten function. Omitting depth is slightly faster. + * + * @param {Array} array + * @param {Array} result + * @return {Array} + */ +function flattenForever (array, result) { + for (var i = 0; i < array.length; i++) { + var value = array[i] + + if (Array.isArray(value)) { + flattenForever(value, result) + } else { + result.push(value) + } + } + + return result +} + +/** + * Flatten an array, with the ability to define a depth. + * + * @param {Array} array + * @param {Number} depth + * @return {Array} + */ +function arrayFlatten (array, depth) { + if (depth == null) { + return flattenForever(array, []) + } + + return flattenWithDepth(array, [], depth) +} diff --git a/daydream/node_modules/array-flatten/package.json b/daydream/node_modules/array-flatten/package.json new file mode 100644 index 000000000..4f01576b1 --- /dev/null +++ b/daydream/node_modules/array-flatten/package.json @@ -0,0 +1,64 @@ +{ + "_from": "array-flatten@1.1.1", + "_id": "array-flatten@1.1.1", + "_inBundle": false, + "_integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "_location": "/array-flatten", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "array-flatten@1.1.1", + "name": "array-flatten", + "escapedName": "array-flatten", + "rawSpec": "1.1.1", + "saveSpec": null, + "fetchSpec": "1.1.1" + }, + "_requiredBy": [ + "/express" + ], + "_resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "_shasum": "9a5f699051b1e7073328f2a008968b64ea2955d2", + "_spec": "array-flatten@1.1.1", + "_where": "/Users/nik/Documents/niksgames.com/nodeapps/udemy/blockland/node_modules/express", + "author": { + "name": "Blake Embrey", + "email": "hello@blakeembrey.com", + "url": "http://blakeembrey.me" + }, + "bugs": { + "url": "https://github.com/blakeembrey/array-flatten/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Flatten an array of nested arrays into a single flat array", + "devDependencies": { + "istanbul": "^0.3.13", + "mocha": "^2.2.4", + "pre-commit": "^1.0.7", + "standard": "^3.7.3" + }, + "files": [ + "array-flatten.js", + "LICENSE" + ], + "homepage": "https://github.com/blakeembrey/array-flatten", + "keywords": [ + "array", + "flatten", + "arguments", + "depth" + ], + "license": "MIT", + "main": "array-flatten.js", + "name": "array-flatten", + "repository": { + "type": "git", + "url": "git://github.com/blakeembrey/array-flatten.git" + }, + "scripts": { + "test": "istanbul cover _mocha -- -R spec" + }, + "version": "1.1.1" +} diff --git a/daydream/node_modules/arraybuffer.slice/.npmignore b/daydream/node_modules/arraybuffer.slice/.npmignore new file mode 100644 index 000000000..cfbee8d8b --- /dev/null +++ b/daydream/node_modules/arraybuffer.slice/.npmignore @@ -0,0 +1,17 @@ +lib-cov +lcov.info +*.seed +*.log +*.csv +*.dat +*.out +*.pid +*.gz + +pids +logs +results +build +.grunt + +node_modules diff --git a/daydream/node_modules/arraybuffer.slice/LICENCE b/daydream/node_modules/arraybuffer.slice/LICENCE new file mode 100644 index 000000000..35fa37590 --- /dev/null +++ b/daydream/node_modules/arraybuffer.slice/LICENCE @@ -0,0 +1,18 @@ +Copyright (C) 2013 Rase- + +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. diff --git a/daydream/node_modules/arraybuffer.slice/Makefile b/daydream/node_modules/arraybuffer.slice/Makefile new file mode 100644 index 000000000..849887f7f --- /dev/null +++ b/daydream/node_modules/arraybuffer.slice/Makefile @@ -0,0 +1,8 @@ + +REPORTER = dot + +test: + @./node_modules/.bin/mocha \ + --reporter $(REPORTER) + +.PHONY: test diff --git a/daydream/node_modules/arraybuffer.slice/README.md b/daydream/node_modules/arraybuffer.slice/README.md new file mode 100644 index 000000000..15e465efc --- /dev/null +++ b/daydream/node_modules/arraybuffer.slice/README.md @@ -0,0 +1,17 @@ +# How to +```javascript +var sliceBuffer = require('arraybuffer.slice'); +var ab = (new Int8Array(5)).buffer; +var sliced = sliceBuffer(ab, 1, 3); +sliced = sliceBuffer(ab, 1); +``` + +# Licence (MIT) +Copyright (C) 2013 Rase- + + +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. diff --git a/daydream/node_modules/arraybuffer.slice/index.js b/daydream/node_modules/arraybuffer.slice/index.js new file mode 100644 index 000000000..11ac556e9 --- /dev/null +++ b/daydream/node_modules/arraybuffer.slice/index.js @@ -0,0 +1,29 @@ +/** + * An abstraction for slicing an arraybuffer even when + * ArrayBuffer.prototype.slice is not supported + * + * @api public + */ + +module.exports = function(arraybuffer, start, end) { + var bytes = arraybuffer.byteLength; + start = start || 0; + end = end || bytes; + + if (arraybuffer.slice) { return arraybuffer.slice(start, end); } + + if (start < 0) { start += bytes; } + if (end < 0) { end += bytes; } + if (end > bytes) { end = bytes; } + + if (start >= bytes || start >= end || bytes === 0) { + return new ArrayBuffer(0); + } + + var abv = new Uint8Array(arraybuffer); + var result = new Uint8Array(end - start); + for (var i = start, ii = 0; i < end; i++, ii++) { + result[ii] = abv[i]; + } + return result.buffer; +}; diff --git a/daydream/node_modules/arraybuffer.slice/package.json b/daydream/node_modules/arraybuffer.slice/package.json new file mode 100644 index 000000000..f89f710b6 --- /dev/null +++ b/daydream/node_modules/arraybuffer.slice/package.json @@ -0,0 +1,44 @@ +{ + "_from": "arraybuffer.slice@~0.0.7", + "_id": "arraybuffer.slice@0.0.7", + "_inBundle": false, + "_integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "_location": "/arraybuffer.slice", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "arraybuffer.slice@~0.0.7", + "name": "arraybuffer.slice", + "escapedName": "arraybuffer.slice", + "rawSpec": "~0.0.7", + "saveSpec": null, + "fetchSpec": "~0.0.7" + }, + "_requiredBy": [ + "/engine.io-parser" + ], + "_resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "_shasum": "3bbc4275dd584cc1b10809b89d4e8b63a69e7675", + "_spec": "arraybuffer.slice@~0.0.7", + "_where": "/Users/nik/Documents/niksgames.com/nodeapps/udemy/blockland/node_modules/engine.io-parser", + "bugs": { + "url": "https://github.com/rase-/arraybuffer.slice/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "Exports a function for slicing ArrayBuffers (no polyfilling)", + "devDependencies": { + "expect.js": "0.2.0", + "mocha": "1.17.1" + }, + "homepage": "https://github.com/rase-/arraybuffer.slice", + "license": "MIT", + "name": "arraybuffer.slice", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/rase-/arraybuffer.slice.git" + }, + "version": "0.0.7" +} diff --git a/daydream/node_modules/arraybuffer.slice/test/slice-buffer.js b/daydream/node_modules/arraybuffer.slice/test/slice-buffer.js new file mode 100644 index 000000000..4778da67d --- /dev/null +++ b/daydream/node_modules/arraybuffer.slice/test/slice-buffer.js @@ -0,0 +1,227 @@ +/* + * Test dependencies + */ + +var sliceBuffer = require('../index.js'); +var expect = require('expect.js'); + +/** + * Tests + */ + +describe('sliceBuffer', function() { + describe('using standard slice', function() { + it('should slice correctly with only start provided', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + + var sliced = sliceBuffer(abv.buffer, 3); + var sabv = new Uint8Array(sliced); + for (var i = 3, ii = 0; i < abv.length; i++, ii++) { + expect(abv[i]).to.equal(sabv[ii]); + } + }); + + it('should slice correctly with start and end provided', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + + var sliced = sliceBuffer(abv.buffer, 3, 8); + var sabv = new Uint8Array(sliced); + for (var i = 3, ii = 0; i < 8; i++, ii++) { + expect(abv[i]).to.equal(sabv[ii]); + } + }); + + it('should slice correctly with negative start', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + + var sliced = sliceBuffer(abv.buffer, -3); + var sabv = new Uint8Array(sliced); + for (var i = abv.length - 3, ii = 0; i < abv.length; i++, ii++) { + expect(abv[i]).to.equal(sabv[ii]); + } + }); + + it('should slice correctly with negative end', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + + var sliced = sliceBuffer(abv.buffer, 0, -3); + var sabv = new Uint8Array(sliced); + for (var i = 0, ii = 0; i < abv.length - 3; i++, ii++) { + expect(abv[i]).to.equal(sabv[ii]); + } + }); + + it('should slice correctly with negative start and end', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + + var sliced = sliceBuffer(abv.buffer, -6, -3); + var sabv = new Uint8Array(sliced); + for (var i = abv.length - 6, ii = 0; i < abv.length - 3; i++, ii++) { + expect(abv[i]).to.equal(sabv[ii]); + } + }); + + it('should slice correctly with equal start and end', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + + var sliced = sliceBuffer(abv.buffer, 1, 1); + expect(sliced.byteLength).to.equal(0); + }); + + it('should slice correctly when end larger than buffer', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + + var sliced = sliceBuffer(abv.buffer, 0, 100); + expect(new Uint8Array(sliced)).to.eql(abv); + }); + + it('shoud slice correctly when start larger than end', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + + var sliced = sliceBuffer(abv.buffer, 6, 5); + expect(sliced.byteLength).to.equal(0); + }); + }); + + describe('using fallback', function() { + it('should slice correctly with only start provided', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + var ab = abv.buffer; + ab.slice = undefined; + + var sliced = sliceBuffer(ab, 3); + var sabv = new Uint8Array(sliced); + for (var i = 3, ii = 0; i < abv.length; i++, ii++) { + expect(abv[i]).to.equal(sabv[ii]); + } + }); + + it('should slice correctly with start and end provided', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + var ab = abv.buffer; + ab.slice = undefined; + + + var sliced = sliceBuffer(ab, 3, 8); + var sabv = new Uint8Array(sliced); + for (var i = 3, ii = 0; i < 8; i++, ii++) { + expect(abv[i]).to.equal(sabv[ii]); + } + }); + + it('should slice correctly with negative start', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + var ab = abv.buffer; + ab.slice = undefined; + + + var sliced = sliceBuffer(ab, -3); + var sabv = new Uint8Array(sliced); + for (var i = abv.length - 3, ii = 0; i < abv.length; i++, ii++) { + expect(abv[i]).to.equal(sabv[ii]); + } + }); + + it('should slice correctly with negative end', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + var ab = abv.buffer; + ab.slice = undefined; + + var sliced = sliceBuffer(ab, 0, -3); + var sabv = new Uint8Array(sliced); + for (var i = 0, ii = 0; i < abv.length - 3; i++, ii++) { + expect(abv[i]).to.equal(sabv[ii]); + } + }); + + it('should slice correctly with negative start and end', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + var ab = abv.buffer; + ab.slice = undefined; + + var sliced = sliceBuffer(ab, -6, -3); + var sabv = new Uint8Array(sliced); + for (var i = abv.length - 6, ii = 0; i < abv.length - 3; i++, ii++) { + expect(abv[i]).to.equal(sabv[ii]); + } + }); + + it('should slice correctly with equal start and end', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + var ab = abv.buffer; + ab.slice = undefined; + + var sliced = sliceBuffer(ab, 1, 1); + expect(sliced.byteLength).to.equal(0); + }); + + it('should slice correctly when end larger than buffer', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + var ab = abv.buffer; + ab.slice = undefined; + + var sliced = sliceBuffer(ab, 0, 100); + var sabv = new Uint8Array(sliced); + for (var i = 0; i < abv.length; i++) { + expect(abv[i]).to.equal(sabv[i]); + } + }); + + it('shoud slice correctly when start larger than end', function() { + var abv = new Uint8Array(10); + for (var i = 0; i < abv.length; i++) { + abv[i] = i; + } + var ab = abv.buffer; + ab.slice = undefined; + + var sliced = sliceBuffer(ab, 6, 5); + expect(sliced.byteLength).to.equal(0); + }); + }); +}); diff --git a/daydream/node_modules/async-limiter/.travis.yml b/daydream/node_modules/async-limiter/.travis.yml new file mode 100644 index 000000000..6cf4a7ad0 --- /dev/null +++ b/daydream/node_modules/async-limiter/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +node_js: + - "6" + - "node" +script: npm run travis +cache: + yarn: true diff --git a/daydream/node_modules/async-limiter/LICENSE b/daydream/node_modules/async-limiter/LICENSE new file mode 100644 index 000000000..9c91fb268 --- /dev/null +++ b/daydream/node_modules/async-limiter/LICENSE @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2017 Samuel Reed + +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. diff --git a/daydream/node_modules/async-limiter/coverage/coverage.json b/daydream/node_modules/async-limiter/coverage/coverage.json new file mode 100644 index 000000000..5b4a35848 --- /dev/null +++ b/daydream/node_modules/async-limiter/coverage/coverage.json @@ -0,0 +1 @@ +{"/Users/samuelreed/git/forks/async-throttle/index.js":{"path":"/Users/samuelreed/git/forks/async-throttle/index.js","s":{"1":1,"2":7,"3":1,"4":6,"5":6,"6":6,"7":6,"8":6,"9":6,"10":1,"11":1,"12":3,"13":13,"14":13,"15":13,"16":1,"17":19,"18":1,"19":45,"20":6,"21":39,"22":13,"23":13,"24":13,"25":13,"26":39,"27":18,"28":6,"29":6,"30":1,"31":6,"32":6,"33":6,"34":1,"35":13,"36":13,"37":1},"b":{"1":[1,6],"2":[6,5],"3":[6,5],"4":[6,39],"5":[13,26],"6":[18,21],"7":[6,0]},"f":{"1":7,"2":3,"3":13,"4":19,"5":45,"6":6,"7":13},"fnMap":{"1":{"name":"Queue","line":3,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":24}}},"2":{"name":"(anonymous_2)","line":22,"loc":{"start":{"line":22,"column":24},"end":{"line":22,"column":41}}},"3":{"name":"(anonymous_3)","line":23,"loc":{"start":{"line":23,"column":28},"end":{"line":23,"column":39}}},"4":{"name":"(anonymous_4)","line":31,"loc":{"start":{"line":31,"column":7},"end":{"line":31,"column":18}}},"5":{"name":"(anonymous_5)","line":36,"loc":{"start":{"line":36,"column":23},"end":{"line":36,"column":34}}},"6":{"name":"(anonymous_6)","line":55,"loc":{"start":{"line":55,"column":25},"end":{"line":55,"column":38}}},"7":{"name":"done","line":62,"loc":{"start":{"line":62,"column":0},"end":{"line":62,"column":16}}}},"statementMap":{"1":{"start":{"line":3,"column":0},"end":{"line":14,"column":1}},"2":{"start":{"line":4,"column":2},"end":{"line":6,"column":3}},"3":{"start":{"line":5,"column":4},"end":{"line":5,"column":30}},"4":{"start":{"line":8,"column":2},"end":{"line":8,"column":26}},"5":{"start":{"line":9,"column":2},"end":{"line":9,"column":53}},"6":{"start":{"line":10,"column":2},"end":{"line":10,"column":19}},"7":{"start":{"line":11,"column":2},"end":{"line":11,"column":17}},"8":{"start":{"line":12,"column":2},"end":{"line":12,"column":16}},"9":{"start":{"line":13,"column":2},"end":{"line":13,"column":31}},"10":{"start":{"line":16,"column":0},"end":{"line":20,"column":2}},"11":{"start":{"line":22,"column":0},"end":{"line":28,"column":3}},"12":{"start":{"line":23,"column":2},"end":{"line":27,"column":4}},"13":{"start":{"line":24,"column":4},"end":{"line":24,"column":75}},"14":{"start":{"line":25,"column":4},"end":{"line":25,"column":16}},"15":{"start":{"line":26,"column":4},"end":{"line":26,"column":24}},"16":{"start":{"line":30,"column":0},"end":{"line":34,"column":3}},"17":{"start":{"line":32,"column":4},"end":{"line":32,"column":43}},"18":{"start":{"line":36,"column":0},"end":{"line":53,"column":2}},"19":{"start":{"line":37,"column":2},"end":{"line":39,"column":3}},"20":{"start":{"line":38,"column":4},"end":{"line":38,"column":11}},"21":{"start":{"line":40,"column":2},"end":{"line":45,"column":3}},"22":{"start":{"line":41,"column":4},"end":{"line":41,"column":32}},"23":{"start":{"line":42,"column":4},"end":{"line":42,"column":19}},"24":{"start":{"line":43,"column":4},"end":{"line":43,"column":20}},"25":{"start":{"line":44,"column":4},"end":{"line":44,"column":16}},"26":{"start":{"line":47,"column":2},"end":{"line":52,"column":3}},"27":{"start":{"line":48,"column":4},"end":{"line":51,"column":5}},"28":{"start":{"line":49,"column":6},"end":{"line":49,"column":30}},"29":{"start":{"line":50,"column":6},"end":{"line":50,"column":27}},"30":{"start":{"line":55,"column":0},"end":{"line":60,"column":2}},"31":{"start":{"line":56,"column":2},"end":{"line":59,"column":3}},"32":{"start":{"line":57,"column":4},"end":{"line":57,"column":22}},"33":{"start":{"line":58,"column":4},"end":{"line":58,"column":16}},"34":{"start":{"line":62,"column":0},"end":{"line":65,"column":1}},"35":{"start":{"line":63,"column":2},"end":{"line":63,"column":17}},"36":{"start":{"line":64,"column":2},"end":{"line":64,"column":14}},"37":{"start":{"line":67,"column":0},"end":{"line":67,"column":23}}},"branchMap":{"1":{"line":4,"type":"if","locations":[{"start":{"line":4,"column":2},"end":{"line":4,"column":2}},{"start":{"line":4,"column":2},"end":{"line":4,"column":2}}]},"2":{"line":8,"type":"binary-expr","locations":[{"start":{"line":8,"column":12},"end":{"line":8,"column":19}},{"start":{"line":8,"column":23},"end":{"line":8,"column":25}}]},"3":{"line":9,"type":"binary-expr","locations":[{"start":{"line":9,"column":21},"end":{"line":9,"column":40}},{"start":{"line":9,"column":44},"end":{"line":9,"column":52}}]},"4":{"line":37,"type":"if","locations":[{"start":{"line":37,"column":2},"end":{"line":37,"column":2}},{"start":{"line":37,"column":2},"end":{"line":37,"column":2}}]},"5":{"line":40,"type":"if","locations":[{"start":{"line":40,"column":2},"end":{"line":40,"column":2}},{"start":{"line":40,"column":2},"end":{"line":40,"column":2}}]},"6":{"line":47,"type":"if","locations":[{"start":{"line":47,"column":2},"end":{"line":47,"column":2}},{"start":{"line":47,"column":2},"end":{"line":47,"column":2}}]},"7":{"line":56,"type":"if","locations":[{"start":{"line":56,"column":2},"end":{"line":56,"column":2}},{"start":{"line":56,"column":2},"end":{"line":56,"column":2}}]}}}} \ No newline at end of file diff --git a/daydream/node_modules/async-limiter/coverage/lcov-report/async-throttle/index.html b/daydream/node_modules/async-limiter/coverage/lcov-report/async-throttle/index.html new file mode 100644 index 000000000..198882b4b --- /dev/null +++ b/daydream/node_modules/async-limiter/coverage/lcov-report/async-throttle/index.html @@ -0,0 +1,73 @@ + + + + Code coverage report for async-throttle/ + + + + + + +
+

Code coverage report for async-throttle/

+

+ Statements: 100% (37 / 37)      + Branches: 92.86% (13 / 14)      + Functions: 100% (7 / 7)      + Lines: 100% (37 / 37)      + Ignored: none      +

+
All files » async-throttle/
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
index.js100%(37 / 37)92.86%(13 / 14)100%(7 / 7)100%(37 / 37)
+
+
+ + + + + + diff --git a/daydream/node_modules/async-limiter/coverage/lcov-report/async-throttle/index.js.html b/daydream/node_modules/async-limiter/coverage/lcov-report/async-throttle/index.js.html new file mode 100644 index 000000000..adc030fda --- /dev/null +++ b/daydream/node_modules/async-limiter/coverage/lcov-report/async-throttle/index.js.html @@ -0,0 +1,246 @@ + + + + Code coverage report for async-throttle/index.js + + + + + + +
+

Code coverage report for async-throttle/index.js

+

+ Statements: 100% (37 / 37)      + Branches: 92.86% (13 / 14)      + Functions: 100% (7 / 7)      + Lines: 100% (37 / 37)      + Ignored: none      +

+
All files » async-throttle/ » index.js
+
+
+

+
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68  +  +1 +7 +1 +  +  +6 +6 +6 +6 +6 +6 +  +  +1 +  +  +  +  +  +1 +3 +13 +13 +13 +  +  +  +1 +  +19 +  +  +  +1 +45 +6 +  +39 +13 +13 +13 +13 +  +  +39 +18 +6 +6 +  +  +  +  +1 +6 +6 +6 +  +  +  +1 +13 +13 +  +  +1 + 
'use strict';
+ 
+function Queue(options) {
+  if (!(this instanceof Queue)) {
+    return new Queue(options);
+  }
+ 
+  options = options || {};
+  this.concurrency = options.concurrency || Infinity;
+  this.pending = 0;
+  this.jobs = [];
+  this.cbs = [];
+  this._done = done.bind(this);
+}
+ 
+var arrayAddMethods = [
+  'push',
+  'unshift',
+  'splice'
+];
+ 
+arrayAddMethods.forEach(function(method) {
+  Queue.prototype[method] = function() {
+    var methodResult = Array.prototype[method].apply(this.jobs, arguments);
+    this._run();
+    return methodResult;
+  };
+});
+ 
+Object.defineProperty(Queue.prototype, 'length', {
+  get: function() {
+    return this.pending + this.jobs.length;
+  }
+});
+ 
+Queue.prototype._run = function() {
+  if (this.pending === this.concurrency) {
+    return;
+  }
+  if (this.jobs.length) {
+    var job = this.jobs.shift();
+    this.pending++;
+    job(this._done);
+    this._run();
+  }
+ 
+  if (this.pending === 0) {
+    while (this.cbs.length !== 0) {
+      var cb = this.cbs.pop();
+      process.nextTick(cb);
+    }
+  }
+};
+ 
+Queue.prototype.onDone = function(cb) {
+  Eif (typeof cb === 'function') {
+    this.cbs.push(cb);
+    this._run();
+  }
+};
+ 
+function done() {
+  this.pending--;
+  this._run();
+}
+ 
+module.exports = Queue;
+ 
+ +
+ + + + + + diff --git a/daydream/node_modules/async-limiter/coverage/lcov-report/base.css b/daydream/node_modules/async-limiter/coverage/lcov-report/base.css new file mode 100644 index 000000000..a6a2f3284 --- /dev/null +++ b/daydream/node_modules/async-limiter/coverage/lcov-report/base.css @@ -0,0 +1,182 @@ +body, html { + margin:0; padding: 0; +} +body { + font-family: Helvetica Neue, Helvetica,Arial; + font-size: 10pt; +} +div.header, div.footer { + background: #eee; + padding: 1em; +} +div.header { + z-index: 100; + position: fixed; + top: 0; + border-bottom: 1px solid #666; + width: 100%; +} +div.footer { + border-top: 1px solid #666; +} +div.body { + margin-top: 10em; +} +div.meta { + font-size: 90%; + text-align: center; +} +h1, h2, h3 { + font-weight: normal; +} +h1 { + font-size: 12pt; +} +h2 { + font-size: 10pt; +} +pre { + font-family: Consolas, Menlo, Monaco, monospace; + margin: 0; + padding: 0; + line-height: 1.3; + font-size: 14px; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} + +div.path { font-size: 110%; } +div.path a:link, div.path a:visited { color: #000; } +table.coverage { border-collapse: collapse; margin:0; padding: 0 } + +table.coverage td { + margin: 0; + padding: 0; + color: #111; + vertical-align: top; +} +table.coverage td.line-count { + width: 50px; + text-align: right; + padding-right: 5px; +} +table.coverage td.line-coverage { + color: #777 !important; + text-align: right; + border-left: 1px solid #666; + border-right: 1px solid #666; +} + +table.coverage td.text { +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 40px; +} +table.coverage td span.cline-neutral { + background: #eee; +} +table.coverage td span.cline-yes { + background: #b5d592; + color: #999; +} +table.coverage td span.cline-no { + background: #fc8c84; +} + +.cstat-yes { color: #111; } +.cstat-no { background: #fc8c84; color: #111; } +.fstat-no { background: #ffc520; color: #111 !important; } +.cbranch-no { background: yellow !important; color: #111; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +.missing-if-branch { + display: inline-block; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: black; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} + +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} + +.entity, .metric { font-weight: bold; } +.metric { display: inline-block; border: 1px solid #333; padding: 0.3em; background: white; } +.metric small { font-size: 80%; font-weight: normal; color: #666; } + +div.coverage-summary table { border-collapse: collapse; margin: 3em; font-size: 110%; } +div.coverage-summary td, div.coverage-summary table th { margin: 0; padding: 0.25em 1em; border-top: 1px solid #666; border-bottom: 1px solid #666; } +div.coverage-summary th { text-align: left; border: 1px solid #666; background: #eee; font-weight: normal; } +div.coverage-summary th.file { border-right: none !important; } +div.coverage-summary th.pic { border-left: none !important; text-align: right; } +div.coverage-summary th.pct { border-right: none !important; } +div.coverage-summary th.abs { border-left: none !important; text-align: right; } +div.coverage-summary td.pct { text-align: right; border-left: 1px solid #666; } +div.coverage-summary td.abs { text-align: right; font-size: 90%; color: #444; border-right: 1px solid #666; } +div.coverage-summary td.file { border-left: 1px solid #666; white-space: nowrap; } +div.coverage-summary td.pic { min-width: 120px !important; } +div.coverage-summary a:link { text-decoration: none; color: #000; } +div.coverage-summary a:visited { text-decoration: none; color: #777; } +div.coverage-summary a:hover { text-decoration: underline; } +div.coverage-summary tfoot td { border-top: 1px solid #666; } + +div.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +div.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +div.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} + +.high { background: #b5d592 !important; } +.medium { background: #ffe87c !important; } +.low { background: #fc8c84 !important; } + +span.cover-fill, span.cover-empty { + display:inline-block; + border:1px solid #444; + background: white; + height: 12px; +} +span.cover-fill { + background: #ccc; + border-right: 1px solid #444; +} +span.cover-empty { + background: white; + border-left: none; +} +span.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } diff --git a/daydream/node_modules/async-limiter/coverage/lcov-report/index.html b/daydream/node_modules/async-limiter/coverage/lcov-report/index.html new file mode 100644 index 000000000..782a1cff1 --- /dev/null +++ b/daydream/node_modules/async-limiter/coverage/lcov-report/index.html @@ -0,0 +1,73 @@ + + + + Code coverage report for All files + + + + + + +
+

Code coverage report for All files

+

+ Statements: 100% (37 / 37)      + Branches: 92.86% (13 / 14)      + Functions: 100% (7 / 7)      + Lines: 100% (37 / 37)      + Ignored: none      +

+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
async-throttle/100%(37 / 37)92.86%(13 / 14)100%(7 / 7)100%(37 / 37)
+
+
+ + + + + + diff --git a/daydream/node_modules/async-limiter/coverage/lcov-report/prettify.css b/daydream/node_modules/async-limiter/coverage/lcov-report/prettify.css new file mode 100644 index 000000000..b317a7cda --- /dev/null +++ b/daydream/node_modules/async-limiter/coverage/lcov-report/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/daydream/node_modules/async-limiter/coverage/lcov-report/prettify.js b/daydream/node_modules/async-limiter/coverage/lcov-report/prettify.js new file mode 100644 index 000000000..ef51e0386 --- /dev/null +++ b/daydream/node_modules/async-limiter/coverage/lcov-report/prettify.js @@ -0,0 +1 @@ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/daydream/node_modules/async-limiter/coverage/lcov-report/sort-arrow-sprite.png b/daydream/node_modules/async-limiter/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 000000000..03f704a60 Binary files /dev/null and b/daydream/node_modules/async-limiter/coverage/lcov-report/sort-arrow-sprite.png differ diff --git a/daydream/node_modules/async-limiter/coverage/lcov-report/sorter.js b/daydream/node_modules/async-limiter/coverage/lcov-report/sorter.js new file mode 100644 index 000000000..6afb736c3 --- /dev/null +++ b/daydream/node_modules/async-limiter/coverage/lcov-report/sorter.js @@ -0,0 +1,156 @@ +var addSorting = (function () { + "use strict"; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { return document.querySelector('.coverage-summary table'); } + // returns the thead element of the summary table + function getTableHeader() { return getTable().querySelector('thead tr'); } + // returns the tbody element of the summary table + function getTableBody() { return getTable().querySelector('tbody'); } + // returns the th element for nth column + function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function (a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function (a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function () { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i =0 ; i < cols.length; i += 1) { + if (cols[i].sortable) { + el = getNthColumn(i).querySelector('.sorter'); + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function () { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(cols); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/daydream/node_modules/async-limiter/coverage/lcov.info b/daydream/node_modules/async-limiter/coverage/lcov.info new file mode 100644 index 000000000..fbf36aab0 --- /dev/null +++ b/daydream/node_modules/async-limiter/coverage/lcov.info @@ -0,0 +1,74 @@ +TN: +SF:/Users/samuelreed/git/forks/async-throttle/index.js +FN:3,Queue +FN:22,(anonymous_2) +FN:23,(anonymous_3) +FN:31,(anonymous_4) +FN:36,(anonymous_5) +FN:55,(anonymous_6) +FN:62,done +FNF:7 +FNH:7 +FNDA:7,Queue +FNDA:3,(anonymous_2) +FNDA:13,(anonymous_3) +FNDA:19,(anonymous_4) +FNDA:45,(anonymous_5) +FNDA:6,(anonymous_6) +FNDA:13,done +DA:3,1 +DA:4,7 +DA:5,1 +DA:8,6 +DA:9,6 +DA:10,6 +DA:11,6 +DA:12,6 +DA:13,6 +DA:16,1 +DA:22,1 +DA:23,3 +DA:24,13 +DA:25,13 +DA:26,13 +DA:30,1 +DA:32,19 +DA:36,1 +DA:37,45 +DA:38,6 +DA:40,39 +DA:41,13 +DA:42,13 +DA:43,13 +DA:44,13 +DA:47,39 +DA:48,18 +DA:49,6 +DA:50,6 +DA:55,1 +DA:56,6 +DA:57,6 +DA:58,6 +DA:62,1 +DA:63,13 +DA:64,13 +DA:67,1 +LF:37 +LH:37 +BRDA:4,1,0,1 +BRDA:4,1,1,6 +BRDA:8,2,0,6 +BRDA:8,2,1,5 +BRDA:9,3,0,6 +BRDA:9,3,1,5 +BRDA:37,4,0,6 +BRDA:37,4,1,39 +BRDA:40,5,0,13 +BRDA:40,5,1,26 +BRDA:47,6,0,18 +BRDA:47,6,1,21 +BRDA:56,7,0,6 +BRDA:56,7,1,0 +BRF:14 +BRH:13 +end_of_record diff --git a/daydream/node_modules/async-limiter/index.js b/daydream/node_modules/async-limiter/index.js new file mode 100644 index 000000000..c9bd2f977 --- /dev/null +++ b/daydream/node_modules/async-limiter/index.js @@ -0,0 +1,67 @@ +'use strict'; + +function Queue(options) { + if (!(this instanceof Queue)) { + return new Queue(options); + } + + options = options || {}; + this.concurrency = options.concurrency || Infinity; + this.pending = 0; + this.jobs = []; + this.cbs = []; + this._done = done.bind(this); +} + +var arrayAddMethods = [ + 'push', + 'unshift', + 'splice' +]; + +arrayAddMethods.forEach(function(method) { + Queue.prototype[method] = function() { + var methodResult = Array.prototype[method].apply(this.jobs, arguments); + this._run(); + return methodResult; + }; +}); + +Object.defineProperty(Queue.prototype, 'length', { + get: function() { + return this.pending + this.jobs.length; + } +}); + +Queue.prototype._run = function() { + if (this.pending === this.concurrency) { + return; + } + if (this.jobs.length) { + var job = this.jobs.shift(); + this.pending++; + job(this._done); + this._run(); + } + + if (this.pending === 0) { + while (this.cbs.length !== 0) { + var cb = this.cbs.pop(); + process.nextTick(cb); + } + } +}; + +Queue.prototype.onDone = function(cb) { + if (typeof cb === 'function') { + this.cbs.push(cb); + this._run(); + } +}; + +function done() { + this.pending--; + this._run(); +} + +module.exports = Queue; diff --git a/daydream/node_modules/async-limiter/package.json b/daydream/node_modules/async-limiter/package.json new file mode 100644 index 000000000..cb74a9068 --- /dev/null +++ b/daydream/node_modules/async-limiter/package.json @@ -0,0 +1,69 @@ +{ + "_from": "async-limiter@~1.0.0", + "_id": "async-limiter@1.0.0", + "_inBundle": false, + "_integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "_location": "/async-limiter", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "async-limiter@~1.0.0", + "name": "async-limiter", + "escapedName": "async-limiter", + "rawSpec": "~1.0.0", + "saveSpec": null, + "fetchSpec": "~1.0.0" + }, + "_requiredBy": [ + "/ws" + ], + "_resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "_shasum": "78faed8c3d074ab81f22b4e985d79e8738f720f8", + "_spec": "async-limiter@~1.0.0", + "_where": "/Users/nik/Documents/niksgames.com/nodeapps/udemy/blockland/node_modules/ws", + "author": { + "name": "Samuel Reed" + }, + "bugs": { + "url": "https://github.com/strml/async-limiter/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "asynchronous function queue with adjustable concurrency", + "devDependencies": { + "coveralls": "^2.11.2", + "eslint": "^4.6.1", + "eslint-plugin-mocha": "^4.11.0", + "intelli-espower-loader": "^1.0.1", + "istanbul": "^0.3.2", + "mocha": "^3.5.2", + "power-assert": "^1.4.4" + }, + "homepage": "https://github.com/strml/async-limiter#readme", + "keywords": [ + "throttle", + "async", + "limiter", + "asynchronous", + "job", + "task", + "concurrency", + "concurrent" + ], + "license": "MIT", + "name": "async-limiter", + "repository": { + "type": "git", + "url": "git+https://github.com/strml/async-limiter.git" + }, + "scripts": { + "coverage": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | coveralls", + "example": "node example", + "lint": "eslint .", + "test": "mocha --R intelli-espower-loader test/", + "travis": "npm run lint && npm run coverage" + }, + "version": "1.0.0" +} diff --git a/daydream/node_modules/async-limiter/readme.md b/daydream/node_modules/async-limiter/readme.md new file mode 100644 index 000000000..dcf4932f9 --- /dev/null +++ b/daydream/node_modules/async-limiter/readme.md @@ -0,0 +1,132 @@ +# Async-Limiter + +A module for limiting concurrent asynchronous actions in flight. Forked from [queue](https://github.com/jessetane/queue). + +[![npm](http://img.shields.io/npm/v/async-limiter.svg?style=flat-square)](http://www.npmjs.org/async-limiter) +[![tests](https://img.shields.io/travis/STRML/async-limiter.svg?style=flat-square&branch=master)](https://travis-ci.org/STRML/async-limiter) +[![coverage](https://img.shields.io/coveralls/STRML/async-limiter.svg?style=flat-square&branch=master)](https://coveralls.io/r/STRML/async-limiter) + +This module exports a class `Limiter` that implements some of the `Array` API. +Pass async functions (ones that accept a callback or return a promise) to an instance's additive array methods. + +## Motivation + +Certain functions, like `zlib`, have [undesirable behavior](https://github.com/nodejs/node/issues/8871#issuecomment-250915913) when +run at infinite concurrency. + +In this case, it is actually faster, and takes far less memory, to limit concurrency. + +This module should do the absolute minimum work necessary to queue up functions. PRs are welcome that would +make this module faster or lighter, but new functionality is not desired. + +Style should confirm to nodejs/node style. + +## Example + +``` javascript +var Limiter = require('async-limiter') + +var t = new Limiter({concurrency: 2}); +var results = [] + +// add jobs using the familiar Array API +t.push(function (cb) { + results.push('two') + cb() +}) + +t.push( + function (cb) { + results.push('four') + cb() + }, + function (cb) { + results.push('five') + cb() + } +) + +t.unshift(function (cb) { + results.push('one') + cb() +}) + +t.splice(2, 0, function (cb) { + results.push('three') + cb() +}) + +// Jobs run automatically. If you want a callback when all are done, +// call 'onDone()'. +t.onDone(function () { + console.log('all done:', results) +}) +``` + +## Zlib Example + +```js +const zlib = require('zlib'); +const Limiter = require('async-limiter'); + +const message = {some: "data"}; +const payload = new Buffer(JSON.stringify(message)); + +// Try with different concurrency values to see how this actually +// slows significantly with higher concurrency! +// +// 5: 1398.607ms +// 10: 1375.668ms +// Infinity: 4423.300ms +// +const t = new Limiter({concurrency: 5}); +function deflate(payload, cb) { + t.push(function(done) { + zlib.deflate(payload, function(err, buffer) { + done(); + cb(err, buffer); + }); + }); +} + +console.time('deflate'); +for(let i = 0; i < 30000; ++i) { + deflate(payload, function (err, buffer) {}); +} +q.onDone(function() { + console.timeEnd('deflate'); +}); +``` + +## Install + +`npm install async-limiter` + +## Test + +`npm test` + +## API + +### `var t = new Limiter([opts])` +Constructor. `opts` may contain inital values for: +* `q.concurrency` + +## Instance methods + +### `q.onDone(fn)` +`fn` will be called once and only once, when the queue is empty. + +## Instance methods mixed in from `Array` +Mozilla has docs on how these methods work [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array). +### `q.push(element1, ..., elementN)` +### `q.unshift(element1, ..., elementN)` +### `q.splice(index , howMany[, element1[, ...[, elementN]]])` + +## Properties +### `q.concurrency` +Max number of jobs the queue should process concurrently, defaults to `Infinity`. + +### `q.length` +Jobs pending + jobs to process (readonly). + diff --git a/daydream/node_modules/backo2/.npmignore b/daydream/node_modules/backo2/.npmignore new file mode 100644 index 000000000..c2658d7d1 --- /dev/null +++ b/daydream/node_modules/backo2/.npmignore @@ -0,0 +1 @@ +node_modules/ diff --git a/daydream/node_modules/backo2/History.md b/daydream/node_modules/backo2/History.md new file mode 100644 index 000000000..8eb28b8e7 --- /dev/null +++ b/daydream/node_modules/backo2/History.md @@ -0,0 +1,12 @@ + +1.0.1 / 2014-02-17 +================== + + * go away decimal point + * history + +1.0.0 / 2014-02-17 +================== + + * add jitter option + * Initial commit diff --git a/daydream/node_modules/backo2/Makefile b/daydream/node_modules/backo2/Makefile new file mode 100644 index 000000000..9987df81a --- /dev/null +++ b/daydream/node_modules/backo2/Makefile @@ -0,0 +1,8 @@ + +test: + @./node_modules/.bin/mocha \ + --require should \ + --reporter dot \ + --bail + +.PHONY: test \ No newline at end of file diff --git a/daydream/node_modules/backo2/Readme.md b/daydream/node_modules/backo2/Readme.md new file mode 100644 index 000000000..0df2a399b --- /dev/null +++ b/daydream/node_modules/backo2/Readme.md @@ -0,0 +1,34 @@ +# backo + + Simple exponential backoff because the others seem to have weird abstractions. + +## Installation + +``` +$ npm install backo +``` + +## Options + + - `min` initial timeout in milliseconds [100] + - `max` max timeout [10000] + - `jitter` [0] + - `factor` [2] + +## Example + +```js +var Backoff = require('backo'); +var backoff = new Backoff({ min: 100, max: 20000 }); + +setTimeout(function(){ + something.reconnect(); +}, backoff.duration()); + +// later when something works +backoff.reset() +``` + +# License + + MIT diff --git a/daydream/node_modules/backo2/component.json b/daydream/node_modules/backo2/component.json new file mode 100644 index 000000000..994845ac9 --- /dev/null +++ b/daydream/node_modules/backo2/component.json @@ -0,0 +1,11 @@ +{ + "name": "backo", + "repo": "segmentio/backo", + "dependencies": {}, + "version": "1.0.1", + "description": "simple backoff without the weird abstractions", + "keywords": ["backoff"], + "license": "MIT", + "scripts": ["index.js"], + "main": "index.js" +} diff --git a/daydream/node_modules/backo2/index.js b/daydream/node_modules/backo2/index.js new file mode 100644 index 000000000..fac4429bf --- /dev/null +++ b/daydream/node_modules/backo2/index.js @@ -0,0 +1,85 @@ + +/** + * Expose `Backoff`. + */ + +module.exports = Backoff; + +/** + * Initialize backoff timer with `opts`. + * + * - `min` initial timeout in milliseconds [100] + * - `max` max timeout [10000] + * - `jitter` [0] + * - `factor` [2] + * + * @param {Object} opts + * @api public + */ + +function Backoff(opts) { + opts = opts || {}; + this.ms = opts.min || 100; + this.max = opts.max || 10000; + this.factor = opts.factor || 2; + this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0; + this.attempts = 0; +} + +/** + * Return the backoff duration. + * + * @return {Number} + * @api public + */ + +Backoff.prototype.duration = function(){ + var ms = this.ms * Math.pow(this.factor, this.attempts++); + if (this.jitter) { + var rand = Math.random(); + var deviation = Math.floor(rand * this.jitter * ms); + ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation; + } + return Math.min(ms, this.max) | 0; +}; + +/** + * Reset the number of attempts. + * + * @api public + */ + +Backoff.prototype.reset = function(){ + this.attempts = 0; +}; + +/** + * Set the minimum duration + * + * @api public + */ + +Backoff.prototype.setMin = function(min){ + this.ms = min; +}; + +/** + * Set the maximum duration + * + * @api public + */ + +Backoff.prototype.setMax = function(max){ + this.max = max; +}; + +/** + * Set the jitter + * + * @api public + */ + +Backoff.prototype.setJitter = function(jitter){ + this.jitter = jitter; +}; + diff --git a/daydream/node_modules/backo2/package.json b/daydream/node_modules/backo2/package.json new file mode 100644 index 000000000..d2e96e87a --- /dev/null +++ b/daydream/node_modules/backo2/package.json @@ -0,0 +1,47 @@ +{ + "_from": "backo2@1.0.2", + "_id": "backo2@1.0.2", + "_inBundle": false, + "_integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "_location": "/backo2", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "backo2@1.0.2", + "name": "backo2", + "escapedName": "backo2", + "rawSpec": "1.0.2", + "saveSpec": null, + "fetchSpec": "1.0.2" + }, + "_requiredBy": [ + "/socket.io-client" + ], + "_resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "_shasum": "31ab1ac8b129363463e35b3ebb69f4dfcfba7947", + "_spec": "backo2@1.0.2", + "_where": "/Users/nik/Documents/niksgames.com/nodeapps/udemy/blockland/node_modules/socket.io-client", + "bugs": { + "url": "https://github.com/mokesmokes/backo/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "simple backoff based on segmentio/backo", + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "homepage": "https://github.com/mokesmokes/backo#readme", + "keywords": [ + "backoff" + ], + "license": "MIT", + "name": "backo2", + "repository": { + "type": "git", + "url": "git+https://github.com/mokesmokes/backo.git" + }, + "version": "1.0.2" +} diff --git a/daydream/node_modules/backo2/test/index.js b/daydream/node_modules/backo2/test/index.js new file mode 100644 index 000000000..ea1f6de13 --- /dev/null +++ b/daydream/node_modules/backo2/test/index.js @@ -0,0 +1,18 @@ + +var Backoff = require('..'); +var assert = require('assert'); + +describe('.duration()', function(){ + it('should increase the backoff', function(){ + var b = new Backoff; + + assert(100 == b.duration()); + assert(200 == b.duration()); + assert(400 == b.duration()); + assert(800 == b.duration()); + + b.reset(); + assert(100 == b.duration()); + assert(200 == b.duration()); + }) +}) \ No newline at end of file diff --git a/daydream/node_modules/base64-arraybuffer/.npmignore b/daydream/node_modules/base64-arraybuffer/.npmignore new file mode 100644 index 000000000..332ee5ada --- /dev/null +++ b/daydream/node_modules/base64-arraybuffer/.npmignore @@ -0,0 +1,3 @@ +/node_modules/ +Gruntfile.js +/test/ diff --git a/daydream/node_modules/base64-arraybuffer/.travis.yml b/daydream/node_modules/base64-arraybuffer/.travis.yml new file mode 100644 index 000000000..19259a549 --- /dev/null +++ b/daydream/node_modules/base64-arraybuffer/.travis.yml @@ -0,0 +1,19 @@ +language: node_js +node_js: +- '0.12' +- iojs-1 +- iojs-2 +- iojs-3 +- '4.1' +before_script: +- npm install +before_install: npm install -g npm@'>=2.13.5' +deploy: + provider: npm + email: niklasvh@gmail.com + api_key: + secure: oHV9ArprTj5WOk7MP1UF7QMJ70huXw+y7xXb5wF4+V2H8Hyfa5TfE0DiOmqrube1WXTeH1FLgq54shp/sJWi47Hkg/GyeoB5NnsPhYEaJkaON9UG5blML+ODiNVsEnq/1kNBQ8e0+0JItMPLGySKyFmuZ3yflulXKS8O88mfINo= + on: + tags: true + branch: master + repo: niklasvh/base64-arraybuffer diff --git a/daydream/node_modules/base64-arraybuffer/LICENSE-MIT b/daydream/node_modules/base64-arraybuffer/LICENSE-MIT new file mode 100644 index 000000000..ed27b41b2 --- /dev/null +++ b/daydream/node_modules/base64-arraybuffer/LICENSE-MIT @@ -0,0 +1,22 @@ +Copyright (c) 2012 Niklas von Hertzen + +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. diff --git a/daydream/node_modules/base64-arraybuffer/README.md b/daydream/node_modules/base64-arraybuffer/README.md new file mode 100644 index 000000000..50009e44f --- /dev/null +++ b/daydream/node_modules/base64-arraybuffer/README.md @@ -0,0 +1,20 @@ +# base64-arraybuffer + +[![Build Status](https://travis-ci.org/niklasvh/base64-arraybuffer.png)](https://travis-ci.org/niklasvh/base64-arraybuffer) +[![NPM Downloads](https://img.shields.io/npm/dm/base64-arraybuffer.svg)](https://www.npmjs.org/package/base64-arraybuffer) +[![NPM Version](https://img.shields.io/npm/v/base64-arraybuffer.svg)](https://www.npmjs.org/package/base64-arraybuffer) + +Encode/decode base64 data into ArrayBuffers + +## Getting Started +Install the module with: `npm install base64-arraybuffer` + +## API +The library encodes and decodes base64 to and from ArrayBuffers + + - __encode(buffer)__ - Encodes `ArrayBuffer` into base64 string + - __decode(str)__ - Decodes base64 string to `ArrayBuffer` + +## License +Copyright (c) 2012 Niklas von Hertzen +Licensed under the MIT license. diff --git a/daydream/node_modules/base64-arraybuffer/lib/base64-arraybuffer.js b/daydream/node_modules/base64-arraybuffer/lib/base64-arraybuffer.js new file mode 100644 index 000000000..e6b630637 --- /dev/null +++ b/daydream/node_modules/base64-arraybuffer/lib/base64-arraybuffer.js @@ -0,0 +1,67 @@ +/* + * base64-arraybuffer + * https://github.com/niklasvh/base64-arraybuffer + * + * Copyright (c) 2012 Niklas von Hertzen + * Licensed under the MIT license. + */ +(function(){ + "use strict"; + + var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + // Use a lookup table to find the index. + var lookup = new Uint8Array(256); + for (var i = 0; i < chars.length; i++) { + lookup[chars.charCodeAt(i)] = i; + } + + exports.encode = function(arraybuffer) { + var bytes = new Uint8Array(arraybuffer), + i, len = bytes.length, base64 = ""; + + for (i = 0; i < len; i+=3) { + base64 += chars[bytes[i] >> 2]; + base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; + base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; + base64 += chars[bytes[i + 2] & 63]; + } + + if ((len % 3) === 2) { + base64 = base64.substring(0, base64.length - 1) + "="; + } else if (len % 3 === 1) { + base64 = base64.substring(0, base64.length - 2) + "=="; + } + + return base64; + }; + + exports.decode = function(base64) { + var bufferLength = base64.length * 0.75, + len = base64.length, i, p = 0, + encoded1, encoded2, encoded3, encoded4; + + if (base64[base64.length - 1] === "=") { + bufferLength--; + if (base64[base64.length - 2] === "=") { + bufferLength--; + } + } + + var arraybuffer = new ArrayBuffer(bufferLength), + bytes = new Uint8Array(arraybuffer); + + for (i = 0; i < len; i+=4) { + encoded1 = lookup[base64.charCodeAt(i)]; + encoded2 = lookup[base64.charCodeAt(i+1)]; + encoded3 = lookup[base64.charCodeAt(i+2)]; + encoded4 = lookup[base64.charCodeAt(i+3)]; + + bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); + bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); + bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); + } + + return arraybuffer; + }; +})(); diff --git a/daydream/node_modules/base64-arraybuffer/package.json b/daydream/node_modules/base64-arraybuffer/package.json new file mode 100644 index 000000000..3a34b462e --- /dev/null +++ b/daydream/node_modules/base64-arraybuffer/package.json @@ -0,0 +1,65 @@ +{ + "_from": "base64-arraybuffer@0.1.5", + "_id": "base64-arraybuffer@0.1.5", + "_inBundle": false, + "_integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "_location": "/base64-arraybuffer", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "base64-arraybuffer@0.1.5", + "name": "base64-arraybuffer", + "escapedName": "base64-arraybuffer", + "rawSpec": "0.1.5", + "saveSpec": null, + "fetchSpec": "0.1.5" + }, + "_requiredBy": [ + "/engine.io-parser", + "/socket.io-client" + ], + "_resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "_shasum": "73926771923b5a19747ad666aa5cd4bf9c6e9ce8", + "_spec": "base64-arraybuffer@0.1.5", + "_where": "/Users/nik/Documents/niksgames.com/nodeapps/udemy/blockland/node_modules/engine.io-parser", + "author": { + "name": "Niklas von Hertzen", + "email": "niklasvh@gmail.com", + "url": "http://hertzen.com" + }, + "bugs": { + "url": "https://github.com/niklasvh/base64-arraybuffer/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Encode/decode base64 data into ArrayBuffers", + "devDependencies": { + "grunt": "^0.4.5", + "grunt-cli": "^0.1.13", + "grunt-contrib-jshint": "^0.11.2", + "grunt-contrib-nodeunit": "^0.4.1", + "grunt-contrib-watch": "^0.6.1" + }, + "engines": { + "node": ">= 0.6.0" + }, + "homepage": "https://github.com/niklasvh/base64-arraybuffer", + "keywords": [], + "licenses": [ + { + "type": "MIT", + "url": "https://github.com/niklasvh/base64-arraybuffer/blob/master/LICENSE-MIT" + } + ], + "main": "lib/base64-arraybuffer", + "name": "base64-arraybuffer", + "repository": { + "type": "git", + "url": "git+https://github.com/niklasvh/base64-arraybuffer.git" + }, + "scripts": { + "test": "grunt nodeunit" + }, + "version": "0.1.5" +} diff --git a/daydream/node_modules/base64id/.npmignore b/daydream/node_modules/base64id/.npmignore new file mode 100644 index 000000000..39e9864f5 --- /dev/null +++ b/daydream/node_modules/base64id/.npmignore @@ -0,0 +1,3 @@ +support +test +examples diff --git a/daydream/node_modules/base64id/LICENSE b/daydream/node_modules/base64id/LICENSE new file mode 100644 index 000000000..0d03c830f --- /dev/null +++ b/daydream/node_modules/base64id/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2012-2016 Kristian Faeldt + +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. diff --git a/daydream/node_modules/base64id/README.md b/daydream/node_modules/base64id/README.md new file mode 100644 index 000000000..17689e6f8 --- /dev/null +++ b/daydream/node_modules/base64id/README.md @@ -0,0 +1,18 @@ +base64id +======== + +Node.js module that generates a base64 id. + +Uses crypto.randomBytes when available, falls back to unsafe methods for node.js <= 0.4. + +To increase performance, random bytes are buffered to minimize the number of synchronous calls to crypto.randomBytes. + +## Installation + + $ npm install base64id + +## Usage + + var base64id = require('base64id'); + + var id = base64id.generateId(); diff --git a/daydream/node_modules/base64id/lib/base64id.js b/daydream/node_modules/base64id/lib/base64id.js new file mode 100644 index 000000000..f68815979 --- /dev/null +++ b/daydream/node_modules/base64id/lib/base64id.js @@ -0,0 +1,103 @@ +/*! + * base64id v0.1.0 + */ + +/** + * Module dependencies + */ + +var crypto = require('crypto'); + +/** + * Constructor + */ + +var Base64Id = function() { }; + +/** + * Get random bytes + * + * Uses a buffer if available, falls back to crypto.randomBytes + */ + +Base64Id.prototype.getRandomBytes = function(bytes) { + + var BUFFER_SIZE = 4096 + var self = this; + + bytes = bytes || 12; + + if (bytes > BUFFER_SIZE) { + return crypto.randomBytes(bytes); + } + + var bytesInBuffer = parseInt(BUFFER_SIZE/bytes); + var threshold = parseInt(bytesInBuffer*0.85); + + if (!threshold) { + return crypto.randomBytes(bytes); + } + + if (this.bytesBufferIndex == null) { + this.bytesBufferIndex = -1; + } + + if (this.bytesBufferIndex == bytesInBuffer) { + this.bytesBuffer = null; + this.bytesBufferIndex = -1; + } + + // No buffered bytes available or index above threshold + if (this.bytesBufferIndex == -1 || this.bytesBufferIndex > threshold) { + + if (!this.isGeneratingBytes) { + this.isGeneratingBytes = true; + crypto.randomBytes(BUFFER_SIZE, function(err, bytes) { + self.bytesBuffer = bytes; + self.bytesBufferIndex = 0; + self.isGeneratingBytes = false; + }); + } + + // Fall back to sync call when no buffered bytes are available + if (this.bytesBufferIndex == -1) { + return crypto.randomBytes(bytes); + } + } + + var result = this.bytesBuffer.slice(bytes*this.bytesBufferIndex, bytes*(this.bytesBufferIndex+1)); + this.bytesBufferIndex++; + + return result; +} + +/** + * Generates a base64 id + * + * (Original version from socket.io ) + */ + +Base64Id.prototype.generateId = function () { + var rand = new Buffer(15); // multiple of 3 for base64 + if (!rand.writeInt32BE) { + return Math.abs(Math.random() * Math.random() * Date.now() | 0).toString() + + Math.abs(Math.random() * Math.random() * Date.now() | 0).toString(); + } + this.sequenceNumber = (this.sequenceNumber + 1) | 0; + rand.writeInt32BE(this.sequenceNumber, 11); + if (crypto.randomBytes) { + this.getRandomBytes(12).copy(rand); + } else { + // not secure for node 0.4 + [0, 4, 8].forEach(function(i) { + rand.writeInt32BE(Math.random() * Math.pow(2, 32) | 0, i); + }); + } + return rand.toString('base64').replace(/\//g, '_').replace(/\+/g, '-'); +}; + +/** + * Export + */ + +exports = module.exports = new Base64Id(); diff --git a/daydream/node_modules/base64id/package.json b/daydream/node_modules/base64id/package.json new file mode 100644 index 000000000..be79db8d4 --- /dev/null +++ b/daydream/node_modules/base64id/package.json @@ -0,0 +1,47 @@ +{ + "_from": "base64id@1.0.0", + "_id": "base64id@1.0.0", + "_inBundle": false, + "_integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", + "_location": "/base64id", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "base64id@1.0.0", + "name": "base64id", + "escapedName": "base64id", + "rawSpec": "1.0.0", + "saveSpec": null, + "fetchSpec": "1.0.0" + }, + "_requiredBy": [ + "/engine.io" + ], + "_resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "_shasum": "47688cb99bb6804f0e06d3e763b1c32e57d8e6b6", + "_spec": "base64id@1.0.0", + "_where": "/Users/nik/Documents/niksgames.com/nodeapps/udemy/blockland/node_modules/engine.io", + "author": { + "name": "Kristian Faeldt", + "email": "faeldt_kristian@cyberagent.co.jp" + }, + "bugs": { + "url": "https://github.com/faeldt/base64id/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Generates a base64 id", + "engines": { + "node": ">= 0.4.0" + }, + "homepage": "https://github.com/faeldt/base64id#readme", + "license": "MIT", + "main": "./lib/base64id.js", + "name": "base64id", + "repository": { + "type": "git", + "url": "git+https://github.com/faeldt/base64id.git" + }, + "version": "1.0.0" +} diff --git a/daydream/node_modules/better-assert/.npmignore b/daydream/node_modules/better-assert/.npmignore new file mode 100644 index 000000000..f1250e584 --- /dev/null +++ b/daydream/node_modules/better-assert/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/daydream/node_modules/better-assert/History.md b/daydream/node_modules/better-assert/History.md new file mode 100644 index 000000000..cbb579bea --- /dev/null +++ b/daydream/node_modules/better-assert/History.md @@ -0,0 +1,15 @@ + +1.0.0 / 2013-02-03 +================== + + * Stop using the removed magic __stack global getter + +0.1.0 / 2012-10-04 +================== + + * add throwing of AssertionError for test frameworks etc + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/daydream/node_modules/better-assert/Makefile b/daydream/node_modules/better-assert/Makefile new file mode 100644 index 000000000..36a3ed7d0 --- /dev/null +++ b/daydream/node_modules/better-assert/Makefile @@ -0,0 +1,5 @@ + +test: + @echo "populate me" + +.PHONY: test \ No newline at end of file diff --git a/daydream/node_modules/better-assert/Readme.md b/daydream/node_modules/better-assert/Readme.md new file mode 100644 index 000000000..d8d3a63b6 --- /dev/null +++ b/daydream/node_modules/better-assert/Readme.md @@ -0,0 +1,61 @@ + +# better-assert + + Better c-style assertions using [callsite](https://github.com/visionmedia/callsite) for + self-documenting failure messages. + +## Installation + + $ npm install better-assert + +## Example + + By default assertions are enabled, however the __NO_ASSERT__ environment variable + will deactivate them when truthy. + +```js +var assert = require('better-assert'); + +test(); + +function test() { + var user = { name: 'tobi' }; + assert('tobi' == user.name); + assert('number' == typeof user.age); +} + +AssertionError: 'number' == typeof user.age + at test (/Users/tj/projects/better-assert/example.js:9:3) + at Object. (/Users/tj/projects/better-assert/example.js:4:1) + at Module._compile (module.js:449:26) + at Object.Module._extensions..js (module.js:467:10) + at Module.load (module.js:356:32) + at Function.Module._load (module.js:312:12) + at Module.runMain (module.js:492:10) + at process.startup.processNextTick.process._tickCallback (node.js:244:9) +``` + +## License + +(The MIT License) + +Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca> + +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. \ No newline at end of file diff --git a/daydream/node_modules/better-assert/example.js b/daydream/node_modules/better-assert/example.js new file mode 100644 index 000000000..688c29e8a --- /dev/null +++ b/daydream/node_modules/better-assert/example.js @@ -0,0 +1,10 @@ + +var assert = require('./'); + +test(); + +function test() { + var user = { name: 'tobi' }; + assert('tobi' == user.name); + assert('number' == typeof user.age); +} \ No newline at end of file diff --git a/daydream/node_modules/better-assert/index.js b/daydream/node_modules/better-assert/index.js new file mode 100644 index 000000000..fd1c9b7d1 --- /dev/null +++ b/daydream/node_modules/better-assert/index.js @@ -0,0 +1,38 @@ +/** + * Module dependencies. + */ + +var AssertionError = require('assert').AssertionError + , callsite = require('callsite') + , fs = require('fs') + +/** + * Expose `assert`. + */ + +module.exports = process.env.NO_ASSERT + ? function(){} + : assert; + +/** + * Assert the given `expr`. + */ + +function assert(expr) { + if (expr) return; + + var stack = callsite(); + var call = stack[1]; + var file = call.getFileName(); + var lineno = call.getLineNumber(); + var src = fs.readFileSync(file, 'utf8'); + var line = src.split('\n')[lineno-1]; + var src = line.match(/assert\((.*)\)/)[1]; + + var err = new AssertionError({ + message: src, + stackStartFunction: stack[0].getFunction() + }); + + throw err; +} diff --git a/daydream/node_modules/better-assert/package.json b/daydream/node_modules/better-assert/package.json new file mode 100644 index 000000000..786bcb141 --- /dev/null +++ b/daydream/node_modules/better-assert/package.json @@ -0,0 +1,65 @@ +{ + "_from": "better-assert@~1.0.0", + "_id": "better-assert@1.0.2", + "_inBundle": false, + "_integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "_location": "/better-assert", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "better-assert@~1.0.0", + "name": "better-assert", + "escapedName": "better-assert", + "rawSpec": "~1.0.0", + "saveSpec": null, + "fetchSpec": "~1.0.0" + }, + "_requiredBy": [ + "/parseqs", + "/parseuri" + ], + "_resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "_shasum": "40866b9e1b9e0b55b481894311e68faffaebc522", + "_spec": "better-assert@~1.0.0", + "_where": "/Users/nik/Documents/niksgames.com/nodeapps/udemy/blockland/node_modules/parseqs", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "bugs": { + "url": "https://github.com/visionmedia/better-assert/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "TonyHe", + "email": "coolhzb@163.com" + }, + { + "name": "ForbesLindesay" + } + ], + "dependencies": { + "callsite": "1.0.0" + }, + "deprecated": false, + "description": "Better assertions for node, reporting the expr, filename, lineno etc", + "engines": { + "node": "*" + }, + "homepage": "https://github.com/visionmedia/better-assert#readme", + "keywords": [ + "assert", + "stack", + "trace", + "debug" + ], + "main": "index", + "name": "better-assert", + "repository": { + "type": "git", + "url": "git+https://github.com/visionmedia/better-assert.git" + }, + "version": "1.0.2" +} diff --git a/daydream/node_modules/blob/.idea/blob.iml b/daydream/node_modules/blob/.idea/blob.iml new file mode 100644 index 000000000..24643cc37 --- /dev/null +++ b/daydream/node_modules/blob/.idea/blob.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/daydream/node_modules/blob/.idea/inspectionProfiles/profiles_settings.xml b/daydream/node_modules/blob/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 000000000..0eefe328a --- /dev/null +++ b/daydream/node_modules/blob/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/daydream/node_modules/blob/.idea/markdown-navigator.xml b/daydream/node_modules/blob/.idea/markdown-navigator.xml new file mode 100644 index 000000000..7753d6d55 --- /dev/null +++ b/daydream/node_modules/blob/.idea/markdown-navigator.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/daydream/node_modules/blob/.idea/markdown-navigator/profiles_settings.xml b/daydream/node_modules/blob/.idea/markdown-navigator/profiles_settings.xml new file mode 100644 index 000000000..57927c5a7 --- /dev/null +++ b/daydream/node_modules/blob/.idea/markdown-navigator/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/daydream/node_modules/blob/.idea/modules.xml b/daydream/node_modules/blob/.idea/modules.xml new file mode 100644 index 000000000..7905a5635 --- /dev/null +++ b/daydream/node_modules/blob/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/daydream/node_modules/blob/.idea/vcs.xml b/daydream/node_modules/blob/.idea/vcs.xml new file mode 100644 index 000000000..94a25f7f4 --- /dev/null +++ b/daydream/node_modules/blob/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/daydream/node_modules/blob/.idea/workspace.xml b/daydream/node_modules/blob/.idea/workspace.xml new file mode 100644 index 000000000..31e803b98 --- /dev/null +++ b/daydream/node_modules/blob/.idea/workspace.xml @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + esprima-six + + + + + + + + + + + + + + true + DEFINITION_ORDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + diff --git a/daydream/public/js/main.js b/daydream/public/js/main.js new file mode 100644 index 000000000..c5c29bc45 --- /dev/null +++ b/daydream/public/js/main.js @@ -0,0 +1,804 @@ +// import {Player} from './player.js'; +// import {PlayerLocal} from './player.js'; +// import {SpeechBubble} from './speech.js'; + +class Game{ + constructor(){ + if ( ! Detector.webgl ) Detector.addGetWebGLMessage(); + + this.modes = Object.freeze({ + NONE: Symbol("none"), + PRELOAD: Symbol("preload"), + INITIALISING: Symbol("initialising"), + CREATING_LEVEL: Symbol("creating_level"), + ACTIVE: Symbol("active"), + GAMEOVER: Symbol("gameover") + }); + this.mode = this.modes.NONE; + + this.container; + this.player; + this.cameras; + this.camera; + this.scene; + this.renderer; + this.animations = {}; + this.assetsPath = 'assets/'; + + this.remotePlayers = []; + this.remoteColliders = []; + this.initialisingPlayers = []; + this.remoteData = []; + + this.messages = { + text:[ + "Welcome to Daydream", + "GOOD LUCK!" + ], + index:0 + } + + this.container = document.createElement( 'div' ); + this.container.style.height = '100%'; + document.body.appendChild( this.container ); + + const sfxExt = SFX.supportsAudioType('mp3') ? 'mp3' : 'ogg'; + + const game = this; + this.anims = ['Walking', 'Walking Backwards', 'Turn', 'Running', 'Pointing', 'Talking', 'Pointing Gesture']; + + const options = { + assets:[ + `${this.assetsPath}images/nx.jpg`, + `${this.assetsPath}images/px.jpg`, + `${this.assetsPath}images/ny.jpg`, + `${this.assetsPath}images/py.jpg`, + `${this.assetsPath}images/nz.jpg`, + `${this.assetsPath}images/pz.jpg` + ], + oncomplete: function(){ + game.init(); + } + } + + this.anims.forEach( function(anim){ options.assets.push(`${game.assetsPath}fbx/anims/${anim}.fbx`)}); + options.assets.push(`${game.assetsPath}fbx/town.fbx`); + + this.mode = this.modes.PRELOAD; + + this.clock = new THREE.Clock(); + + const preloader = new Preloader(options); + + window.onError = function(error){ + console.error(JSON.stringify(error)); + } + } + + initSfx(){ + this.sfx = {}; + this.sfx.context = new (window.AudioContext || window.webkitAudioContext)(); + this.sfx.gliss = new SFX({ + context: this.sfx.context, + src:{mp3:`${this.assetsPath}sfx/gliss.mp3`, ogg:`${this.assetsPath}sfx/gliss.ogg`}, + loop: false, + volume: 0.3 + }); + } + + set activeCamera(object){ + this.cameras.active = object; + } + + init() { + this.mode = this.modes.INITIALISING; + + this.camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 10, 200000 ); + + this.scene = new THREE.Scene(); + this.scene.background = new THREE.Color( 0x00a0f0 ); + + const ambient = new THREE.AmbientLight( 0xaaaaaa ); + this.scene.add( ambient ); + + const light = new THREE.DirectionalLight( 0xaaaaaa ); + light.position.set( 30, 100, 40 ); + light.target.position.set( 0, 0, 0 ); + + light.castShadow = true; + + const lightSize = 500; + light.shadow.camera.near = 1; + light.shadow.camera.far = 500; + light.shadow.camera.left = light.shadow.camera.bottom = -lightSize; + light.shadow.camera.right = light.shadow.camera.top = lightSize; + + light.shadow.bias = 0.0039; + light.shadow.mapSize.width = 1024; + light.shadow.mapSize.height = 1024; + + this.sun = light; + this.scene.add(light); + + // model + const loader = new THREE.FBXLoader(); + const game = this; + + this.player = new PlayerLocal(this); + + this.loadEnvironment(loader); + + this.speechBubble = new SpeechBubble(this, "", 150); + this.speechBubble.mesh.position.set(0, 350, 0); + + this.joystick = new JoyStick({ + onMove: this.playerControl, + game: this + }); + + this.renderer = new THREE.WebGLRenderer( { antialias: true } ); + this.renderer.setPixelRatio( window.devicePixelRatio ); + this.renderer.setSize( window.innerWidth, window.innerHeight ); + this.renderer.shadowMap.enabled = true; + this.container.appendChild( this.renderer.domElement ); + + if ('ontouchstart' in window){ + window.addEventListener( 'touchdown', (event) => game.onMouseDown(event), false ); + }else{ + window.addEventListener( 'mousedown', (event) => game.onMouseDown(event), false ); + } + + window.addEventListener( 'resize', () => game.onWindowResize(), false ); + } + + loadEnvironment(loader){ + const game = this; + loader.load(`${this.assetsPath}fbx/town.fbx`, function(object){ + game.environment = object; + game.colliders = []; + game.scene.add(object); + object.traverse( function ( child ) { + if ( child.isMesh ) { + if (child.name.startsWith("proxy")){ + game.colliders.push(child); + child.material.visible = false; + }else{ + child.castShadow = true; + child.receiveShadow = true; + } + } + } ); + + const tloader = new THREE.CubeTextureLoader(); + tloader.setPath( `${game.assetsPath}/images/` ); + + var textureCube = tloader.load( [ + 'px.jpg', 'nx.jpg', + 'py.jpg', 'ny.jpg', + 'pz.jpg', 'nz.jpg' + ] ); + + game.scene.background = textureCube; + + game.loadNextAnim(loader); + }) + } + + loadNextAnim(loader){ + let anim = this.anims.pop(); + const game = this; + loader.load( `${this.assetsPath}fbx/anims/${anim}.fbx`, function( object ){ + game.player.animations[anim] = object.animations[0]; + if (game.anims.length>0){ + game.loadNextAnim(loader); + }else{ + delete game.anims; + game.action = "Idle"; + game.mode = game.modes.ACTIVE; + game.animate(); + } + }); + } + + playerControl(forward, turn){ + turn = -turn; + + if (forward>0.3){ + if (this.player.action!='Walking' && this.player.action!='Running') this.player.action = 'Walking'; + }else if (forward<-0.3){ + if (this.player.action!='Walking Backwards') this.player.action = 'Walking Backwards'; + }else{ + forward = 0; + if (Math.abs(turn)>0.1){ + if (this.player.action != 'Turn') this.player.action = 'Turn'; + }else if (this.player.action!="Idle"){ + this.player.action = 'Idle'; + } + } + + if (forward==0 && turn==0){ + delete this.player.motion; + }else{ + this.player.motion = { forward, turn }; + } + + this.player.updateSocket(); + } + + createCameras(){ + const offset = new THREE.Vector3(0, 80, 0); + const front = new THREE.Object3D(); + front.position.set(112, 100, 600); + front.parent = this.player.object; + const back = new THREE.Object3D(); + back.position.set(0, 300, -1050); + back.parent = this.player.object; + const chat = new THREE.Object3D(); + chat.position.set(0, 200, -450); + chat.parent = this.player.object; + const wide = new THREE.Object3D(); + wide.position.set(178, 139, 1665); + wide.parent = this.player.object; + const overhead = new THREE.Object3D(); + overhead.position.set(0, 400, 0); + overhead.parent = this.player.object; + const collect = new THREE.Object3D(); + collect.position.set(40, 82, 94); + collect.parent = this.player.object; + this.cameras = { front, back, wide, overhead, collect, chat }; + this.activeCamera = this.cameras.back; + } + + showMessage(msg, fontSize=20, onOK=null){ + const txt = document.getElementById('message_text'); + txt.innerHTML = msg; + txt.style.fontSize = fontSize + 'px'; + const btn = document.getElementById('message_ok'); + const panel = document.getElementById('message'); + const game = this; + if (onOK!=null){ + btn.onclick = function(){ + panel.style.display = 'none'; + onOK.call(game); + } + }else{ + btn.onclick = function(){ + panel.style.display = 'none'; + } + } + panel.style.display = 'flex'; + } + + onWindowResize() { + this.camera.aspect = window.innerWidth / window.innerHeight; + this.camera.updateProjectionMatrix(); + + this.renderer.setSize( window.innerWidth, window.innerHeight ); + + } + + updateRemotePlayers(dt){ + if (this.remoteData===undefined || this.remoteData.length == 0 || this.player===undefined || this.player.id===undefined) return; + + const newPlayers = []; + const game = this; + //Get all remotePlayers from remoteData array + const remotePlayers = []; + const remoteColliders = []; + + this.remoteData.forEach( function(data){ + if (game.player.id != data.id){ + //Is this player being initialised? + let iplayer; + game.initialisingPlayers.forEach( function(player){ + if (player.id == data.id) iplayer = player; + }); + //If not being initialised check the remotePlayers array + if (iplayer===undefined){ + let rplayer; + game.remotePlayers.forEach( function(player){ + if (player.id == data.id) rplayer = player; + }); + if (rplayer===undefined){ + //Initialise player + game.initialisingPlayers.push( new Player( game, data )); + }else{ + //Player exists + remotePlayers.push(rplayer); + remoteColliders.push(rplayer.collider); + } + } + } + }); + + this.scene.children.forEach( function(object){ + if (object.userData.remotePlayer && game.getRemotePlayerById(object.userData.id)==undefined){ + game.scene.remove(object); + } + }); + + this.remotePlayers = remotePlayers; + this.remoteColliders = remoteColliders; + this.remotePlayers.forEach(function(player){ player.update( dt ); }); + } + + onMouseDown( event ) { + if (this.remoteColliders===undefined || this.remoteColliders.length==0 || this.speechBubble===undefined || this.speechBubble.mesh===undefined) return; + + // calculate mouse position in normalized device coordinates + // (-1 to +1) for both components + const mouse = new THREE.Vector2(); + mouse.x = ( event.clientX / this.renderer.domElement.width ) * 2 - 1; + mouse.y = - ( event.clientY / this.renderer.domElement.height ) * 2 + 1; + + const raycaster = new THREE.Raycaster(); + raycaster.setFromCamera( mouse, this.camera ); + + const intersects = raycaster.intersectObjects( this.remoteColliders ); + const chat = document.getElementById('chat'); + + if (intersects.length>0){ + const object = intersects[0].object; + const players = this.remotePlayers.filter( function(player){ + if (player.collider!==undefined && player.collider==object){ + return true; + } + }); + if (players.length>0){ + const player = players[0]; + console.log(`onMouseDown: player ${player.id}`); + this.speechBubble.player = player; + this.speechBubble.update(''); + this.scene.add(this.speechBubble.mesh); + this.chatSocketId = player.id; + chat.style.bottom = '0px'; + this.activeCamera = this.cameras.chat; + } + }else{ + //Is the chat panel visible? + if (chat.style.bottom=='0px' && (window.innerHeight - event.clientY)>40){ + console.log("onMouseDown: No player found"); + if (this.speechBubble.mesh.parent!==null) this.speechBubble.mesh.parent.remove(this.speechBubble.mesh); + delete this.speechBubble.player; + delete this.chatSocketId; + chat.style.bottom = '-50px'; + this.activeCamera = this.cameras.back; + }else{ + console.log("onMouseDown: typing"); + } + } + } + + getRemotePlayerById(id){ + if (this.remotePlayers===undefined || this.remotePlayers.length==0) return; + + const players = this.remotePlayers.filter(function(player){ + if (player.id == id) return true; + }); + + if (players.length==0) return; + + return players[0]; + } + + animate() { + const game = this; + const dt = this.clock.getDelta(); + + requestAnimationFrame( function(){ game.animate(); } ); + + this.updateRemotePlayers(dt); + + if (this.player.mixer!=undefined && this.mode==this.modes.ACTIVE) this.player.mixer.update(dt); + + if (this.player.action=='Walking'){ + const elapsedTime = Date.now() - this.player.actionTime; + if (elapsedTime>1000 && this.player.motion.forward>0){ + this.player.action = 'Running'; + } + } + + if (this.player.motion !== undefined) this.player.move(dt); + + if (this.cameras!=undefined && this.cameras.active!=undefined && this.player!==undefined && this.player.object!==undefined){ + this.camera.position.lerp(this.cameras.active.getWorldPosition(new THREE.Vector3()), 0.05); + const pos = this.player.object.position.clone(); + if (this.cameras.active==this.cameras.chat){ + pos.y += 200; + }else{ + pos.y += 300; + } + this.camera.lookAt(pos); + } + + if (this.sun !== undefined){ + this.sun.position.copy( this.camera.position ); + this.sun.position.y += 10; + } + + if (this.speechBubble!==undefined) this.speechBubble.show(this.camera.position); + + this.renderer.render( this.scene, this.camera ); + } +} + +class Player{ + constructor(game, options){ + this.local = true; + let model, colour; + + const colours = ['Black', 'Brown', 'White']; + colour = colours[Math.floor(Math.random()*colours.length)]; + + if (options===undefined){ + const people = ['BeachBabe', 'BusinessMan', 'Doctor', 'FireFighter', 'Housewife', 'Policeman', 'Prostitute', 'Punk', 'RiotCop', 'Roadworker', 'Robber', 'Sheriff', 'Streetman', 'Waitress']; + model = people[Math.floor(Math.random()*people.length)]; + }else if (typeof options =='object'){ + this.local = false; + this.options = options; + this.id = options.id; + model = options.model; + colour = options.colour; + }else{ + model = options; + } + this.model = model; + this.colour = colour; + this.game = game; + this.animations = this.game.animations; + + const loader = new THREE.FBXLoader(); + const player = this; + + loader.load( `${game.assetsPath}fbx/people/${model}.fbx`, function ( object ) { + + object.mixer = new THREE.AnimationMixer( object ); + player.root = object; + player.mixer = object.mixer; + + object.name = "Person"; + + object.traverse( function ( child ) { + if ( child.isMesh ) { + child.castShadow = true; + child.receiveShadow = true; + } + } ); + + + const textureLoader = new THREE.TextureLoader(); + + textureLoader.load(`${game.assetsPath}images/SimplePeople_${model}_${colour}.png`, function(texture){ + object.traverse( function ( child ) { + if ( child.isMesh ){ + child.material.map = texture; + } + } ); + }); + + player.object = new THREE.Object3D(); + player.object.position.set(3122, 0, -173); + player.object.rotation.set(0, 2.6, 0); + + player.object.add(object); + if (player.deleted===undefined) game.scene.add(player.object); + + if (player.local){ + game.createCameras(); + game.sun.target = game.player.object; + game.animations.Idle = object.animations[0]; + if (player.initSocket!==undefined) player.initSocket(); + }else{ + const geometry = new THREE.BoxGeometry(100,300,100); + const material = new THREE.MeshBasicMaterial({visible:false}); + const box = new THREE.Mesh(geometry, material); + box.name = "Collider"; + box.position.set(0, 150, 0); + player.object.add(box); + player.collider = box; + player.object.userData.id = player.id; + player.object.userData.remotePlayer = true; + const players = game.initialisingPlayers.splice(game.initialisingPlayers.indexOf(this), 1); + game.remotePlayers.push(players[0]); + } + + if (game.animations.Idle!==undefined) player.action = "Idle"; + } ); + } + + set action(name){ + //Make a copy of the clip if this is a remote player + if (this.actionName == name) return; + const clip = (this.local) ? this.animations[name] : THREE.AnimationClip.parse(THREE.AnimationClip.toJSON(this.animations[name])); + const action = this.mixer.clipAction( clip ); + action.time = 0; + this.mixer.stopAllAction(); + this.actionName = name; + this.actionTime = Date.now(); + + action.fadeIn(0.5); + action.play(); + } + + get action(){ + return this.actionName; + } + + update(dt){ + this.mixer.update(dt); + + if (this.game.remoteData.length>0){ + let found = false; + for(let data of this.game.remoteData){ + if (data.id != this.id) continue; + //Found the player + this.object.position.set( data.x, data.y, data.z ); + const euler = new THREE.Euler(data.pb, data.heading, data.pb); + this.object.quaternion.setFromEuler( euler ); + this.action = data.action; + found = true; + } + if (!found) this.game.removePlayer(this); + } + } +} + +class PlayerLocal extends Player{ + constructor(game, model){ + super(game, model); + + const player = this; + const socket = io(); + socket.on('setId', function(data){ + player.id = data.id; + }); + socket.on('remoteData', function(data){ + game.remoteData = data; + }); + socket.on('deletePlayer', function(data){ + const players = game.remotePlayers.filter(function(player){ + if (player.id == data.id){ + return player; + } + }); + if (players.length>0){ + let index = game.remotePlayers.indexOf(players[0]); + if (index!=-1){ + game.remotePlayers.splice( index, 1 ); + game.scene.remove(players[0].object); + }else{ + index = game.initialisingPlayers.indexOf(data.id); + if (index!=-1){ + const player = game.initialisingPlayers[index]; + player.deleted = true; + game.initialisingPlayers.splice(index, 1); + } + } + } + }) + socket.on('chat message', function(data){ + document.getElementById('chat').style.bottom = '0px'; + const player = game.getRemotePlayerById(data.id); + game.speechBubble.player = player; + game.chatSocketId = player.id; + game.activeCamera = game.cameras.chat; + game.speechBubble.update(data.message); + }); + $('#msg-form').submit(function(e){ + socket.emit('chat message', { id:game.chatSocketId, message:$('#m').val() }); + $('#m').val(''); + return false; + }); + + this.socket = socket; + } + + initSocket(){ + //console.log("PlayerLocal.initSocket"); + this.socket.emit('init', { + model:this.model, + colour: this.colour, + x: this.object.position.x, + y: this.object.position.y, + z: this.object.position.z, + h: this.object.rotation.y, + pb: this.object.rotation.x + }); + } + + updateSocket(){ + if (this.socket !== undefined){ + //console.log(`PlayerLocal.updateSocket - rotation(${this.object.rotation.x.toFixed(1)},${this.object.rotation.y.toFixed(1)},${this.object.rotation.z.toFixed(1)})`); + this.socket.emit('update', { + x: this.object.position.x, + y: this.object.position.y, + z: this.object.position.z, + h: this.object.rotation.y, + pb: this.object.rotation.x, + action: this.action + }) + } + } + + move(dt){ + const pos = this.object.position.clone(); + pos.y += 60; + let dir = new THREE.Vector3(); + this.object.getWorldDirection(dir); + if (this.motion.forward<0) dir.negate(); + let raycaster = new THREE.Raycaster(pos, dir); + let blocked = false; + const colliders = this.game.colliders; + + if (colliders!==undefined){ + const intersect = raycaster.intersectObjects(colliders); + if (intersect.length>0){ + if (intersect[0].distance<50) blocked = true; + } + } + + if (!blocked){ + if (this.motion.forward>0){ + const speed = (this.action=='Running') ? 500 : 150; + this.object.translateZ(dt*speed); + }else{ + this.object.translateZ(-dt*30); + } + } + + if (colliders!==undefined){ + //cast left + dir.set(-1,0,0); + dir.applyMatrix4(this.object.matrix); + dir.normalize(); + raycaster = new THREE.Raycaster(pos, dir); + + let intersect = raycaster.intersectObjects(colliders); + if (intersect.length>0){ + if (intersect[0].distance<50) this.object.translateX(100-intersect[0].distance); + } + + //cast right + dir.set(1,0,0); + dir.applyMatrix4(this.object.matrix); + dir.normalize(); + raycaster = new THREE.Raycaster(pos, dir); + + intersect = raycaster.intersectObjects(colliders); + if (intersect.length>0){ + if (intersect[0].distance<50) this.object.translateX(intersect[0].distance-100); + } + + //cast down + dir.set(0,-1,0); + pos.y += 200; + raycaster = new THREE.Raycaster(pos, dir); + const gravity = 30; + + intersect = raycaster.intersectObjects(colliders); + if (intersect.length>0){ + const targetY = pos.y - intersect[0].distance; + if (targetY > this.object.position.y){ + //Going up + this.object.position.y = 0.8 * this.object.position.y + 0.2 * targetY; + this.velocityY = 0; + }else if (targetY < this.object.position.y){ + //Falling + if (this.velocityY==undefined) this.velocityY = 0; + this.velocityY += dt * gravity; + this.object.position.y -= this.velocityY; + if (this.object.position.y < targetY){ + this.velocityY = 0; + this.object.position.y = targetY; + } + } + } + } + + this.object.rotateY(this.motion.turn*dt); + + this.updateSocket(); + } +} + +class SpeechBubble{ + constructor(game, msg, size=1){ + this.config = { font:'Calibri', size:24, padding:10, colour:'#222', width:256, height:256 }; + + const planeGeometry = new THREE.PlaneGeometry(size, size); + const planeMaterial = new THREE.MeshBasicMaterial() + this.mesh = new THREE.Mesh(planeGeometry, planeMaterial); + game.scene.add(this.mesh); + + const self = this; + const loader = new THREE.TextureLoader(); + loader.load( + // resource URL + `${game.assetsPath}images/speech.png`, + + // onLoad callback + function ( texture ) { + // in this example we create the material when the texture is loaded + self.img = texture.image; + self.mesh.material.map = texture; + self.mesh.material.transparent = true; + self.mesh.material.needsUpdate = true; + if (msg!==undefined) self.update(msg); + }, + + // onProgress callback currently not supported + undefined, + + // onError callback + function ( err ) { + console.error( 'An error happened.' ); + } + ); + } + + update(msg){ + if (this.mesh===undefined) return; + + let context = this.context; + + if (this.mesh.userData.context===undefined){ + const canvas = this.createOffscreenCanvas(this.config.width, this.config.height); + this.context = canvas.getContext('2d'); + context = this.context; + context.font = `${this.config.size}pt ${this.config.font}`; + context.fillStyle = this.config.colour; + context.textAlign = 'center'; + this.mesh.material.map = new THREE.CanvasTexture(canvas); + } + + const bg = this.img; + context.clearRect(0, 0, this.config.width, this.config.height); + context.drawImage(bg, 0, 0, bg.width, bg.height, 0, 0, this.config.width, this.config.height); + this.wrapText(msg, context); + + this.mesh.material.map.needsUpdate = true; + } + + createOffscreenCanvas(w, h) { + const canvas = document.createElement('canvas'); + canvas.width = w; + canvas.height = h; + return canvas; + } + + wrapText(text, context){ + const words = text.split(' '); + let line = ''; + const lines = []; + const maxWidth = this.config.width - 2*this.config.padding; + const lineHeight = this.config.size + 8; + + words.forEach( function(word){ + const testLine = `${line}${word} `; + const metrics = context.measureText(testLine); + const testWidth = metrics.width; + if (testWidth > maxWidth) { + lines.push(line); + line = `${word} `; + }else { + line = testLine; + } + }); + + if (line != '') lines.push(line); + + let y = (this.config.height - lines.length * lineHeight)/2; + + lines.forEach( function(line){ + context.fillText(line, 128, y); + y += lineHeight; + }); + } + + show(pos){ + if (this.mesh!==undefined && this.player!==undefined){ + this.mesh.position.set(this.player.object.position.x, this.player.object.position.y + 380, this.player.object.position.z); + this.mesh.lookAt(pos); + } + } +} diff --git a/daydream/public/js/player.js b/daydream/public/js/player.js new file mode 100644 index 000000000..fabaaa30c --- /dev/null +++ b/daydream/public/js/player.js @@ -0,0 +1,122 @@ + + +class Player{ + constructor(game, options){ + this.local = true; + let model, colour; + + const colours = ['Black', 'Brown', 'White']; + colour = colours[Math.floor(Math.random()*colours.length)]; + + if (options===undefined){ + const people = ['BeachBabe', 'BusinessMan', 'Doctor', 'FireFighter', 'Housewife', 'Policeman', 'Prostitute', 'Punk', 'RiotCop', 'Roadworker', 'Robber', 'Sheriff', 'Streetman', 'Waitress']; + model = people[Math.floor(Math.random()*people.length)]; + }else if (typeof options =='object'){ + this.local = false; + this.options = options; + this.id = options.id; + model = options.model; + colour = options.colour; + }else{ + model = options; + } + this.model = model; + this.colour = colour; + this.game = game; + this.animations = this.game.animations; + + const loader = new THREE.FBXLoader(); + const player = this; + + loader.load( `${game.assetsPath}fbx/people/${model}.fbx`, function ( object ) { + + object.mixer = new THREE.AnimationMixer( object ); + player.root = object; + player.mixer = object.mixer; + + object.name = "Person"; + + object.traverse( function ( child ) { + if ( child.isMesh ) { + child.castShadow = true; + child.receiveShadow = true; + } + } ); + + + const textureLoader = new THREE.TextureLoader(); + + textureLoader.load(`${game.assetsPath}images/SimplePeople_${model}_${colour}.png`, function(texture){ + object.traverse( function ( child ) { + if ( child.isMesh ){ + child.material.map = texture; + } + } ); + }); + + player.object = new THREE.Object3D(); + player.object.position.set(3122, 0, -173); + player.object.rotation.set(0, 2.6, 0); + + player.object.add(object); + if (player.deleted===undefined) game.scene.add(player.object); + + if (player.local){ + game.createCameras(); + game.sun.target = game.player.object; + game.animations.Idle = object.animations[0]; + if (player.initSocket!==undefined) player.initSocket(); + }else{ + const geometry = new THREE.BoxGeometry(100,300,100); + const material = new THREE.MeshBasicMaterial({visible:false}); + const box = new THREE.Mesh(geometry, material); + box.name = "Collider"; + box.position.set(0, 150, 0); + player.object.add(box); + player.collider = box; + player.object.userData.id = player.id; + player.object.userData.remotePlayer = true; + const players = game.initialisingPlayers.splice(game.initialisingPlayers.indexOf(this), 1); + game.remotePlayers.push(players[0]); + } + + if (game.animations.Idle!==undefined) player.action = "Idle"; + } ); + } + + set action(name){ + //Make a copy of the clip if this is a remote player + if (this.actionName == name) return; + const clip = (this.local) ? this.animations[name] : THREE.AnimationClip.parse(THREE.AnimationClip.toJSON(this.animations[name])); + const action = this.mixer.clipAction( clip ); + action.time = 0; + this.mixer.stopAllAction(); + this.actionName = name; + this.actionTime = Date.now(); + + action.fadeIn(0.5); + action.play(); + } + + get action(){ + return this.actionName; + } + + update(dt){ + this.mixer.update(dt); + + if (this.game.remoteData.length>0){ + let found = false; + for(let data of this.game.remoteData){ + if (data.id != this.id) continue; + //Found the player + this.object.position.set( data.x, data.y, data.z ); + const euler = new THREE.Euler(data.pb, data.heading, data.pb); + this.object.quaternion.setFromEuler( euler ); + this.action = data.action; + found = true; + } + if (!found) this.game.removePlayer(this); + } + } +} diff --git a/daydream/public/js/speech.js b/daydream/public/js/speech.js new file mode 100644 index 000000000..42c903837 --- /dev/null +++ b/daydream/public/js/speech.js @@ -0,0 +1,105 @@ + + +class SpeechBubble{ + constructor(game, msg, size=1){ + this.config = { font:'Calibri', size:24, padding:10, colour:'#222', width:256, height:256 }; + + const planeGeometry = new THREE.PlaneGeometry(size, size); + const planeMaterial = new THREE.MeshBasicMaterial() + this.mesh = new THREE.Mesh(planeGeometry, planeMaterial); + game.scene.add(this.mesh); + + const self = this; + const loader = new THREE.TextureLoader(); + loader.load( + // resource URL + `${game.assetsPath}images/speech.png`, + + // onLoad callback + function ( texture ) { + // in this example we create the material when the texture is loaded + self.img = texture.image; + self.mesh.material.map = texture; + self.mesh.material.transparent = true; + self.mesh.material.needsUpdate = true; + if (msg!==undefined) self.update(msg); + }, + + // onProgress callback currently not supported + undefined, + + // onError callback + function ( err ) { + console.error( 'An error happened.' ); + } + ); + } + + update(msg){ + if (this.mesh===undefined) return; + + let context = this.context; + + if (this.mesh.userData.context===undefined){ + const canvas = this.createOffscreenCanvas(this.config.width, this.config.height); + this.context = canvas.getContext('2d'); + context = this.context; + context.font = `${this.config.size}pt ${this.config.font}`; + context.fillStyle = this.config.colour; + context.textAlign = 'center'; + this.mesh.material.map = new THREE.CanvasTexture(canvas); + } + + const bg = this.img; + context.clearRect(0, 0, this.config.width, this.config.height); + context.drawImage(bg, 0, 0, bg.width, bg.height, 0, 0, this.config.width, this.config.height); + this.wrapText(msg, context); + + this.mesh.material.map.needsUpdate = true; + } + + createOffscreenCanvas(w, h) { + const canvas = document.createElement('canvas'); + canvas.width = w; + canvas.height = h; + return canvas; + } + + wrapText(text, context){ + const words = text.split(' '); + let line = ''; + const lines = []; + const maxWidth = this.config.width - 2*this.config.padding; + const lineHeight = this.config.size + 8; + + words.forEach( function(word){ + const testLine = `${line}${word} `; + const metrics = context.measureText(testLine); + const testWidth = metrics.width; + if (testWidth > maxWidth) { + lines.push(line); + line = `${word} `; + }else { + line = testLine; + } + }); + + if (line != '') lines.push(line); + + let y = (this.config.height - lines.length * lineHeight)/2; + + lines.forEach( function(line){ + context.fillText(line, 128, y); + y += lineHeight; + }); + } + + show(pos){ + if (this.mesh!==undefined && this.player!==undefined){ + this.mesh.position.set(this.player.object.position.x, this.player.object.position.y + 380, this.player.object.position.z); + this.mesh.lookAt(pos); + } + } +} + +module.exports.SpeechBubble = SpeechBubble; \ No newline at end of file diff --git a/daydream/public/libs/CannonDebugRenderer.js b/daydream/public/libs/CannonDebugRenderer.js new file mode 100644 index 000000000..ea63abe01 --- /dev/null +++ b/daydream/public/libs/CannonDebugRenderer.js @@ -0,0 +1,243 @@ +/* global CANNON,THREE,Detector */ + +/** + * Adds Three.js primitives into the scene where all the Cannon bodies and shapes are. + * @class CannonDebugRenderer + * @param {THREE.Scene} scene + * @param {CANNON.World} world + * @param {object} [options] + */ +THREE.CannonDebugRenderer = function(scene, world, options){ + options = options || {}; + + const group = new THREE.Group(); + group.name = "Physics"; + scene.add(group); + + this.scene = group; + this.world = world; + + this._meshes = []; + + this._material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }); + this._sphereGeometry = new THREE.SphereGeometry(1); + this._boxGeometry = new THREE.BoxGeometry(1, 1, 1); + this._planeGeometry = new THREE.PlaneGeometry( 10, 10, 10, 10 ); + this._cylinderGeometry = new THREE.CylinderGeometry( 1, 1, 10, 10 ); +}; + +THREE.CannonDebugRenderer.prototype = { + + tmpVec0: new CANNON.Vec3(), + tmpVec1: new CANNON.Vec3(), + tmpVec2: new CANNON.Vec3(), + tmpQuat0: new CANNON.Vec3(), + + update: function(){ + + var bodies = this.world.bodies; + var meshes = this._meshes; + var shapeWorldPosition = this.tmpVec0; + var shapeWorldQuaternion = this.tmpQuat0; + + var meshIndex = 0; + + for (var i = 0; i !== bodies.length; i++) { + var body = bodies[i]; + + for (var j = 0; j !== body.shapes.length; j++) { + var shape = body.shapes[j]; + + this._updateMesh(meshIndex, body, shape); + + var mesh = meshes[meshIndex]; + + if(mesh){ + + // Get world position + body.quaternion.vmult(body.shapeOffsets[j], shapeWorldPosition); + body.position.vadd(shapeWorldPosition, shapeWorldPosition); + + // Get world quaternion + body.quaternion.mult(body.shapeOrientations[j], shapeWorldQuaternion); + + // Copy to meshes + mesh.position.copy(shapeWorldPosition); + mesh.quaternion.copy(shapeWorldQuaternion); + } + + meshIndex++; + } + } + + for(var i = meshIndex; i < meshes.length; i++){ + var mesh = meshes[i]; + if(mesh){ + this.scene.remove(mesh); + } + } + + meshes.length = meshIndex; + }, + + _updateMesh: function(index, body, shape){ + var mesh = this._meshes[index]; + if(!this._typeMatch(mesh, shape)){ + if(mesh){ + this.scene.remove(mesh); + } + mesh = this._meshes[index] = this._createMesh(shape); + } + this._scaleMesh(mesh, shape); + }, + + _typeMatch: function(mesh, shape){ + if(!mesh){ + return false; + } + var geo = mesh.geometry; + return ( + (geo instanceof THREE.SphereGeometry && shape instanceof CANNON.Sphere) || + (geo instanceof THREE.BoxGeometry && shape instanceof CANNON.Box) || + (geo instanceof THREE.PlaneGeometry && shape instanceof CANNON.Plane) || + (geo.id === shape.geometryId && shape instanceof CANNON.ConvexPolyhedron) || + (geo.id === shape.geometryId && shape instanceof CANNON.Trimesh) || + (geo.id === shape.geometryId && shape instanceof CANNON.Heightfield) + ); + }, + + _createMesh: function(shape){ + var mesh; + var material = this._material; + + switch(shape.type){ + + case CANNON.Shape.types.SPHERE: + mesh = new THREE.Mesh(this._sphereGeometry, material); + break; + + case CANNON.Shape.types.BOX: + mesh = new THREE.Mesh(this._boxGeometry, material); + break; + + case CANNON.Shape.types.PLANE: + mesh = new THREE.Mesh(this._planeGeometry, material); + break; + + case CANNON.Shape.types.CONVEXPOLYHEDRON: + // Create mesh + var geo = new THREE.Geometry(); + + // Add vertices + for (var i = 0; i < shape.vertices.length; i++) { + var v = shape.vertices[i]; + geo.vertices.push(new THREE.Vector3(v.x, v.y, v.z)); + } + + for(var i=0; i < shape.faces.length; i++){ + var face = shape.faces[i]; + + // add triangles + var a = face[0]; + for (var j = 1; j < face.length - 1; j++) { + var b = face[j]; + var c = face[j + 1]; + geo.faces.push(new THREE.Face3(a, b, c)); + } + } + geo.computeBoundingSphere(); + geo.computeFaceNormals(); + + mesh = new THREE.Mesh(geo, material); + shape.geometryId = geo.id; + break; + + case CANNON.Shape.types.TRIMESH: + var geometry = new THREE.Geometry(); + var v0 = this.tmpVec0; + var v1 = this.tmpVec1; + var v2 = this.tmpVec2; + for (var i = 0; i < shape.indices.length / 3; i++) { + shape.getTriangleVertices(i, v0, v1, v2); + geometry.vertices.push( + new THREE.Vector3(v0.x, v0.y, v0.z), + new THREE.Vector3(v1.x, v1.y, v1.z), + new THREE.Vector3(v2.x, v2.y, v2.z) + ); + var j = geometry.vertices.length - 3; + geometry.faces.push(new THREE.Face3(j, j+1, j+2)); + } + geometry.computeBoundingSphere(); + geometry.computeFaceNormals(); + mesh = new THREE.Mesh(geometry, material); + shape.geometryId = geometry.id; + break; + + case CANNON.Shape.types.HEIGHTFIELD: + var geometry = new THREE.Geometry(); + + var v0 = this.tmpVec0; + var v1 = this.tmpVec1; + var v2 = this.tmpVec2; + for (var xi = 0; xi < shape.data.length - 1; xi++) { + for (var yi = 0; yi < shape.data[xi].length - 1; yi++) { + for (var k = 0; k < 2; k++) { + shape.getConvexTrianglePillar(xi, yi, k===0); + v0.copy(shape.pillarConvex.vertices[0]); + v1.copy(shape.pillarConvex.vertices[1]); + v2.copy(shape.pillarConvex.vertices[2]); + v0.vadd(shape.pillarOffset, v0); + v1.vadd(shape.pillarOffset, v1); + v2.vadd(shape.pillarOffset, v2); + geometry.vertices.push( + new THREE.Vector3(v0.x, v0.y, v0.z), + new THREE.Vector3(v1.x, v1.y, v1.z), + new THREE.Vector3(v2.x, v2.y, v2.z) + ); + var i = geometry.vertices.length - 3; + geometry.faces.push(new THREE.Face3(i, i+1, i+2)); + } + } + } + geometry.computeBoundingSphere(); + geometry.computeFaceNormals(); + mesh = new THREE.Mesh(geometry, material); + shape.geometryId = geometry.id; + break; + } + + if(mesh){ + this.scene.add(mesh); + } + + return mesh; + }, + + _scaleMesh: function(mesh, shape){ + switch(shape.type){ + + case CANNON.Shape.types.SPHERE: + var radius = shape.radius; + mesh.scale.set(radius, radius, radius); + break; + + case CANNON.Shape.types.BOX: + mesh.scale.copy(shape.halfExtents); + mesh.scale.multiplyScalar(2); + break; + + case CANNON.Shape.types.CONVEXPOLYHEDRON: + mesh.scale.set(1,1,1); + break; + + case CANNON.Shape.types.TRIMESH: + mesh.scale.copy(shape.scale); + break; + + case CANNON.Shape.types.HEIGHTFIELD: + mesh.scale.set(1,1,1); + break; + + } + } +}; \ No newline at end of file diff --git a/daydream/public/libs/Detector.js b/daydream/public/libs/Detector.js new file mode 100644 index 000000000..1f98cca54 --- /dev/null +++ b/daydream/public/libs/Detector.js @@ -0,0 +1,78 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * @author mr.doob / http://mrdoob.com/ + */ + +var Detector = { + + canvas: !! window.CanvasRenderingContext2D, + webgl: ( function () { + + try { + + var canvas = document.createElement( 'canvas' ); return !! ( window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ) ); + + } catch ( e ) { + + return false; + + } + + } )(), + workers: !! window.Worker, + fileapi: window.File && window.FileReader && window.FileList && window.Blob, + + getWebGLErrorMessage: function () { + + var element = document.createElement( 'div' ); + element.id = 'webgl-error-message'; + element.style.fontFamily = 'monospace'; + element.style.fontSize = '13px'; + element.style.fontWeight = 'normal'; + element.style.textAlign = 'center'; + element.style.background = '#fff'; + element.style.color = '#000'; + element.style.padding = '1.5em'; + element.style.width = '400px'; + element.style.margin = '5em auto 0'; + + if ( ! this.webgl ) { + + element.innerHTML = window.WebGLRenderingContext ? [ + 'Your graphics card does not seem to support WebGL.
', + 'Find out how to get it here.' + ].join( '\n' ) : [ + 'Your browser does not seem to support WebGL.
', + 'Find out how to get it here.' + ].join( '\n' ); + + } + + return element; + + }, + + addGetWebGLMessage: function ( parameters ) { + + var parent, id, element; + + parameters = parameters || {}; + + parent = parameters.parent !== undefined ? parameters.parent : document.body; + id = parameters.id !== undefined ? parameters.id : 'oldie'; + + element = Detector.getWebGLErrorMessage(); + element.id = id; + + parent.appendChild( element ); + + } + +}; + +// browserify support +if ( typeof module === 'object' ) { + + module.exports = Detector; + +} \ No newline at end of file diff --git a/daydream/public/libs/FBXLoader.js b/daydream/public/libs/FBXLoader.js new file mode 100644 index 000000000..9f0f85550 --- /dev/null +++ b/daydream/public/libs/FBXLoader.js @@ -0,0 +1,4010 @@ +/** + * @author Kyle-Larson https://github.com/Kyle-Larson + * @author Takahiro https://github.com/takahirox + * @author Lewy Blue https://github.com/looeee + * + * Loader loads FBX file and generates Group representing FBX scene. + * Requires FBX file to be >= 7.0 and in ASCII or >= 6400 in Binary format + * Versions lower than this may load but will probably have errors + * + * Needs Support: + * Morph normals / blend shape normals + * + * FBX format references: + * https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure + * http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_index_html (C++ SDK reference) + * + * Binary format specification: + * https://code.blender.org/2013/08/fbx-binary-file-format-specification/ + */ + + +THREE.FBXLoader = ( function () { + + var fbxTree; + var connections; + var sceneGraph; + + function FBXLoader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + } + + FBXLoader.prototype = { + + constructor: FBXLoader, + + crossOrigin: 'anonymous', + + load: function ( url, onLoad, onProgress, onError ) { + var self = this; + + var resourceDirectory = THREE.LoaderUtils.extractUrlBase( url ); + + var loader = new THREE.FileLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( buffer ) { + + try { + + var scene = self.parse( buffer, resourceDirectory ); + onLoad( scene ); + + } catch ( error ) { + + setTimeout( function () { + + if ( onError ) onError( error ); + + self.manager.itemError( url ); + + }, 0 ); + + } + + }, onProgress, onError ); + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + return this; + + }, + + parse: function ( FBXBuffer, resourceDirectory ) { + + if ( isFbxFormatBinary( FBXBuffer ) ) { + + fbxTree = new BinaryParser().parse( FBXBuffer ); + + } else { + + var FBXText = convertArrayBufferToString( FBXBuffer ); + + if ( ! isFbxFormatASCII( FBXText ) ) { + + throw new Error( 'THREE.FBXLoader: Unknown format.' ); + + } + + if ( getFbxVersion( FBXText ) < 7000 ) { + + throw new Error( 'THREE.FBXLoader: FBX version not supported, FileVersion: ' + getFbxVersion( FBXText ) ); + + } + + fbxTree = new TextParser().parse( FBXText ); + + } + + // console.log( fbxTree ); + + var textureLoader = new THREE.TextureLoader( this.manager ).setPath( resourceDirectory ).setCrossOrigin( this.crossOrigin ); + + return new FBXTreeParser( textureLoader ).parse( fbxTree ); + + } + + }; + + // Parse the FBXTree object returned by the BinaryParser or TextParser and return a THREE.Group + function FBXTreeParser( textureLoader ) { + + this.textureLoader = textureLoader; + + } + + FBXTreeParser.prototype = { + + constructor: FBXTreeParser, + + parse: function () { + + connections = this.parseConnections(); + + var images = this.parseImages(); + var textures = this.parseTextures( images ); + var materials = this.parseMaterials( textures ); + var deformers = this.parseDeformers(); + var geometryMap = new GeometryParser().parse( deformers ); + + this.parseScene( deformers, geometryMap, materials ); + + return sceneGraph; + + }, + + // Parses FBXTree.Connections which holds parent-child connections between objects (e.g. material -> texture, model->geometry ) + // and details the connection type + parseConnections: function () { + + var connectionMap = new Map(); + + if ( 'Connections' in fbxTree ) { + + var rawConnections = fbxTree.Connections.connections; + + rawConnections.forEach( function ( rawConnection ) { + + var fromID = rawConnection[ 0 ]; + var toID = rawConnection[ 1 ]; + var relationship = rawConnection[ 2 ]; + + if ( ! connectionMap.has( fromID ) ) { + + connectionMap.set( fromID, { + parents: [], + children: [] + } ); + + } + + var parentRelationship = { ID: toID, relationship: relationship }; + connectionMap.get( fromID ).parents.push( parentRelationship ); + + if ( ! connectionMap.has( toID ) ) { + + connectionMap.set( toID, { + parents: [], + children: [] + } ); + + } + + var childRelationship = { ID: fromID, relationship: relationship }; + connectionMap.get( toID ).children.push( childRelationship ); + + } ); + + } + + return connectionMap; + + }, + + // Parse FBXTree.Objects.Video for embedded image data + // These images are connected to textures in FBXTree.Objects.Textures + // via FBXTree.Connections. + parseImages: function () { + + var images = {}; + var blobs = {}; + + if ( 'Video' in fbxTree.Objects ) { + + var videoNodes = fbxTree.Objects.Video; + + for ( var nodeID in videoNodes ) { + + var videoNode = videoNodes[ nodeID ]; + + var id = parseInt( nodeID ); + + images[ id ] = videoNode.RelativeFilename || videoNode.Filename; + + // raw image data is in videoNode.Content + if ( 'Content' in videoNode ) { + + var arrayBufferContent = ( videoNode.Content instanceof ArrayBuffer ) && ( videoNode.Content.byteLength > 0 ); + var base64Content = ( typeof videoNode.Content === 'string' ) && ( videoNode.Content !== '' ); + + if ( arrayBufferContent || base64Content ) { + + var image = this.parseImage( videoNodes[ nodeID ] ); + + blobs[ videoNode.RelativeFilename || videoNode.Filename ] = image; + + } + + } + + } + + } + + for ( var id in images ) { + + var filename = images[ id ]; + + if ( blobs[ filename ] !== undefined ) images[ id ] = blobs[ filename ]; + else images[ id ] = images[ id ].split( '\\' ).pop(); + + } + + return images; + + }, + + // Parse embedded image data in FBXTree.Video.Content + parseImage: function ( videoNode ) { + + var content = videoNode.Content; + var fileName = videoNode.RelativeFilename || videoNode.Filename; + var extension = fileName.slice( fileName.lastIndexOf( '.' ) + 1 ).toLowerCase(); + + var type; + + switch ( extension ) { + + case 'bmp': + + type = 'image/bmp'; + break; + + case 'jpg': + case 'jpeg': + + type = 'image/jpeg'; + break; + + case 'png': + + type = 'image/png'; + break; + + case 'tif': + + type = 'image/tiff'; + break; + + case 'tga': + + if ( typeof THREE.TGALoader !== 'function' ) { + + console.warn( 'FBXLoader: THREE.TGALoader is required to load TGA textures' ); + return; + + } else { + + if ( THREE.Loader.Handlers.get( '.tga' ) === null ) { + + THREE.Loader.Handlers.add( /\.tga$/i, new THREE.TGALoader() ); + + } + + type = 'image/tga'; + break; + + } + + default: + + console.warn( 'FBXLoader: Image type "' + extension + '" is not supported.' ); + return; + + } + + if ( typeof content === 'string' ) { // ASCII format + + return 'data:' + type + ';base64,' + content; + + } else { // Binary Format + + var array = new Uint8Array( content ); + return window.URL.createObjectURL( new Blob( [ array ], { type: type } ) ); + + } + + }, + + // Parse nodes in FBXTree.Objects.Texture + // These contain details such as UV scaling, cropping, rotation etc and are connected + // to images in FBXTree.Objects.Video + parseTextures: function ( images ) { + + var textureMap = new Map(); + + if ( 'Texture' in fbxTree.Objects ) { + + var textureNodes = fbxTree.Objects.Texture; + for ( var nodeID in textureNodes ) { + + var texture = this.parseTexture( textureNodes[ nodeID ], images ); + textureMap.set( parseInt( nodeID ), texture ); + + } + + } + + return textureMap; + + }, + + // Parse individual node in FBXTree.Objects.Texture + parseTexture: function ( textureNode, images ) { + + var texture = this.loadTexture( textureNode, images ); + + texture.ID = textureNode.id; + + texture.name = textureNode.attrName; + + var wrapModeU = textureNode.WrapModeU; + var wrapModeV = textureNode.WrapModeV; + + var valueU = wrapModeU !== undefined ? wrapModeU.value : 0; + var valueV = wrapModeV !== undefined ? wrapModeV.value : 0; + + // http://download.autodesk.com/us/fbx/SDKdocs/FBX_SDK_Help/files/fbxsdkref/class_k_fbx_texture.html#889640e63e2e681259ea81061b85143a + // 0: repeat(default), 1: clamp + + texture.wrapS = valueU === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + texture.wrapT = valueV === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + + if ( 'Scaling' in textureNode ) { + + var values = textureNode.Scaling.value; + + texture.repeat.x = values[ 0 ]; + texture.repeat.y = values[ 1 ]; + + } + + return texture; + + }, + + // load a texture specified as a blob or data URI, or via an external URL using THREE.TextureLoader + loadTexture: function ( textureNode, images ) { + + var fileName; + + var currentPath = this.textureLoader.path; + + var children = connections.get( textureNode.id ).children; + + if ( children !== undefined && children.length > 0 && images[ children[ 0 ].ID ] !== undefined ) { + + fileName = images[ children[ 0 ].ID ]; + + if ( fileName.indexOf( 'blob:' ) === 0 || fileName.indexOf( 'data:' ) === 0 ) { + + this.textureLoader.setPath( undefined ); + + } + + } + + var texture; + + var extension = textureNode.FileName.slice( - 3 ).toLowerCase(); + + if ( extension === 'tga' ) { + + var loader = THREE.Loader.Handlers.get( '.tga' ); + + if ( loader === null ) { + + console.warn( 'FBXLoader: TGALoader not found, creating empty placeholder texture for', fileName ); + texture = new THREE.Texture(); + + } else { + + texture = loader.load( fileName ); + + } + + } else if ( extension === 'psd' ) { + + console.warn( 'FBXLoader: PSD textures are not supported, creating empty placeholder texture for', fileName ); + texture = new THREE.Texture(); + + } else { + + //const name = fileName.split('\\').pop().split('/').pop(); + //fileName = `../images/${name}`; + + texture = this.textureLoader.load( fileName ); + + } + + this.textureLoader.setPath( currentPath ); + + return texture; + + }, + + // Parse nodes in FBXTree.Objects.Material + parseMaterials: function ( textureMap ) { + + var materialMap = new Map(); + + if ( 'Material' in fbxTree.Objects ) { + + var materialNodes = fbxTree.Objects.Material; + + for ( var nodeID in materialNodes ) { + + var material = this.parseMaterial( materialNodes[ nodeID ], textureMap ); + + if ( material !== null ) materialMap.set( parseInt( nodeID ), material ); + + } + + } + + return materialMap; + + }, + + // Parse single node in FBXTree.Objects.Material + // Materials are connected to texture maps in FBXTree.Objects.Textures + // FBX format currently only supports Lambert and Phong shading models + parseMaterial: function ( materialNode, textureMap ) { + + var ID = materialNode.id; + var name = materialNode.attrName; + var type = materialNode.ShadingModel; + + // Case where FBX wraps shading model in property object. + if ( typeof type === 'object' ) { + + type = type.value; + + } + + // Ignore unused materials which don't have any connections. + if ( ! connections.has( ID ) ) return null; + + var parameters = this.parseParameters( materialNode, textureMap, ID ); + + var material; + + switch ( type.toLowerCase() ) { + + case 'phong': + material = new THREE.MeshPhongMaterial(); + break; + case 'lambert': + material = new THREE.MeshLambertMaterial(); + break; + default: + console.warn( 'THREE.FBXLoader: unknown material type "%s". Defaulting to MeshPhongMaterial.', type ); + material = new THREE.MeshPhongMaterial( { color: 0x3300ff } ); + break; + + } + + material.setValues( parameters ); + material.name = name; + + return material; + + }, + + // Parse FBX material and return parameters suitable for a three.js material + // Also parse the texture map and return any textures associated with the material + parseParameters: function ( materialNode, textureMap, ID ) { + + var parameters = {}; + + if ( materialNode.BumpFactor ) { + + parameters.bumpScale = materialNode.BumpFactor.value; + + } + if ( materialNode.Diffuse ) { + + parameters.color = new THREE.Color().fromArray( materialNode.Diffuse.value ); + + } else if ( materialNode.DiffuseColor && materialNode.DiffuseColor.type === 'Color' ) { + + // The blender exporter exports diffuse here instead of in materialNode.Diffuse + parameters.color = new THREE.Color().fromArray( materialNode.DiffuseColor.value ); + + } + if ( materialNode.DisplacementFactor ) { + + parameters.displacementScale = materialNode.DisplacementFactor.value; + + } + if ( materialNode.Emissive ) { + + parameters.emissive = new THREE.Color().fromArray( materialNode.Emissive.value ); + + } else if ( materialNode.EmissiveColor && materialNode.EmissiveColor.type === 'Color' ) { + + // The blender exporter exports emissive color here instead of in materialNode.Emissive + parameters.emissive = new THREE.Color().fromArray( materialNode.EmissiveColor.value ); + + } + if ( materialNode.EmissiveFactor ) { + + parameters.emissiveIntensity = parseFloat( materialNode.EmissiveFactor.value ); + + } + if ( materialNode.Opacity ) { + + parameters.opacity = parseFloat( materialNode.Opacity.value ); + + } + if ( parameters.opacity < 1.0 ) { + + parameters.transparent = true; + + } + if ( materialNode.ReflectionFactor ) { + + parameters.reflectivity = materialNode.ReflectionFactor.value; + + } + if ( materialNode.Shininess ) { + + parameters.shininess = materialNode.Shininess.value; + + } + if ( materialNode.Specular ) { + + parameters.specular = new THREE.Color().fromArray( materialNode.Specular.value ); + + } else if ( materialNode.SpecularColor && materialNode.SpecularColor.type === 'Color' ) { + + // The blender exporter exports specular color here instead of in materialNode.Specular + parameters.specular = new THREE.Color().fromArray( materialNode.SpecularColor.value ); + + } + + var self = this; + connections.get( ID ).children.forEach( function ( child ) { + + var type = child.relationship; + + switch ( type ) { + + case 'Bump': + parameters.bumpMap = self.getTexture( textureMap, child.ID ); + break; + + case 'DiffuseColor': + parameters.map = self.getTexture( textureMap, child.ID ); + break; + + case 'DisplacementColor': + parameters.displacementMap = self.getTexture( textureMap, child.ID ); + break; + + + case 'EmissiveColor': + parameters.emissiveMap = self.getTexture( textureMap, child.ID ); + break; + + case 'NormalMap': + parameters.normalMap = self.getTexture( textureMap, child.ID ); + break; + + case 'ReflectionColor': + parameters.envMap = self.getTexture( textureMap, child.ID ); + parameters.envMap.mapping = THREE.EquirectangularReflectionMapping; + break; + + case 'SpecularColor': + parameters.specularMap = self.getTexture( textureMap, child.ID ); + break; + + case 'TransparentColor': + parameters.alphaMap = self.getTexture( textureMap, child.ID ); + parameters.transparent = true; + break; + + case 'AmbientColor': + case 'ShininessExponent': // AKA glossiness map + case 'SpecularFactor': // AKA specularLevel + case 'VectorDisplacementColor': // NOTE: Seems to be a copy of DisplacementColor + default: + console.warn( 'THREE.FBXLoader: %s map is not supported in three.js, skipping texture.', type ); + break; + + } + + } ); + + return parameters; + + }, + + // get a texture from the textureMap for use by a material. + getTexture: function ( textureMap, id ) { + + // if the texture is a layered texture, just use the first layer and issue a warning + if ( 'LayeredTexture' in fbxTree.Objects && id in fbxTree.Objects.LayeredTexture ) { + + console.warn( 'THREE.FBXLoader: layered textures are not supported in three.js. Discarding all but first layer.' ); + id = connections.get( id ).children[ 0 ].ID; + + } + + return textureMap.get( id ); + + }, + + // Parse nodes in FBXTree.Objects.Deformer + // Deformer node can contain skinning or Vertex Cache animation data, however only skinning is supported here + // Generates map of Skeleton-like objects for use later when generating and binding skeletons. + parseDeformers: function () { + + var skeletons = {}; + var morphTargets = {}; + + if ( 'Deformer' in fbxTree.Objects ) { + + var DeformerNodes = fbxTree.Objects.Deformer; + + for ( var nodeID in DeformerNodes ) { + + var deformerNode = DeformerNodes[ nodeID ]; + + var relationships = connections.get( parseInt( nodeID ) ); + + if ( deformerNode.attrType === 'Skin' ) { + + var skeleton = this.parseSkeleton( relationships, DeformerNodes ); + skeleton.ID = nodeID; + + if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: skeleton attached to more than one geometry is not supported.' ); + skeleton.geometryID = relationships.parents[ 0 ].ID; + + skeletons[ nodeID ] = skeleton; + + } else if ( deformerNode.attrType === 'BlendShape' ) { + + var morphTarget = { + id: nodeID, + }; + + morphTarget.rawTargets = this.parseMorphTargets( relationships, DeformerNodes ); + morphTarget.id = nodeID; + + if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: morph target attached to more than one geometry is not supported.' ); + + morphTargets[ nodeID ] = morphTarget; + + } + + } + + } + + return { + + skeletons: skeletons, + morphTargets: morphTargets, + + }; + + }, + + // Parse single nodes in FBXTree.Objects.Deformer + // The top level skeleton node has type 'Skin' and sub nodes have type 'Cluster' + // Each skin node represents a skeleton and each cluster node represents a bone + parseSkeleton: function ( relationships, deformerNodes ) { + + var rawBones = []; + + relationships.children.forEach( function ( child ) { + + var boneNode = deformerNodes[ child.ID ]; + + if ( boneNode.attrType !== 'Cluster' ) return; + + var rawBone = { + + ID: child.ID, + indices: [], + weights: [], + transform: new THREE.Matrix4().fromArray( boneNode.Transform.a ), + transformLink: new THREE.Matrix4().fromArray( boneNode.TransformLink.a ), + linkMode: boneNode.Mode, + + }; + + if ( 'Indexes' in boneNode ) { + + rawBone.indices = boneNode.Indexes.a; + rawBone.weights = boneNode.Weights.a; + + } + + rawBones.push( rawBone ); + + } ); + + return { + + rawBones: rawBones, + bones: [] + + }; + + }, + + // The top level morph deformer node has type "BlendShape" and sub nodes have type "BlendShapeChannel" + parseMorphTargets: function ( relationships, deformerNodes ) { + + var rawMorphTargets = []; + + for ( var i = 0; i < relationships.children.length; i ++ ) { + + var child = relationships.children[ i ]; + + var morphTargetNode = deformerNodes[ child.ID ]; + + var rawMorphTarget = { + + name: morphTargetNode.attrName, + initialWeight: morphTargetNode.DeformPercent, + id: morphTargetNode.id, + fullWeights: morphTargetNode.FullWeights.a + + }; + + if ( morphTargetNode.attrType !== 'BlendShapeChannel' ) return; + + var targetRelationships = connections.get( parseInt( child.ID ) ); + + targetRelationships.children.forEach( function ( child ) { + + if ( child.relationship === undefined ) rawMorphTarget.geoID = child.ID; + + } ); + + rawMorphTargets.push( rawMorphTarget ); + + } + + return rawMorphTargets; + + }, + + // create the main THREE.Group() to be returned by the loader + parseScene: function ( deformers, geometryMap, materialMap ) { + + sceneGraph = new THREE.Group(); + + var modelMap = this.parseModels( deformers.skeletons, geometryMap, materialMap ); + + var modelNodes = fbxTree.Objects.Model; + + var self = this; + modelMap.forEach( function ( model ) { + + var modelNode = modelNodes[ model.ID ]; + self.setLookAtProperties( model, modelNode ); + + var parentConnections = connections.get( model.ID ).parents; + + parentConnections.forEach( function ( connection ) { + + var parent = modelMap.get( connection.ID ); + if ( parent !== undefined ) parent.add( model ); + + } ); + + if ( model.parent === null ) { + + sceneGraph.add( model ); + + } + + + } ); + + this.bindSkeleton( deformers.skeletons, geometryMap, modelMap ); + + this.createAmbientLight(); + + this.setupMorphMaterials(); + + var animations = new AnimationParser().parse(); + + // if all the models where already combined in a single group, just return that + if ( sceneGraph.children.length === 1 && sceneGraph.children[ 0 ].isGroup ) { + + sceneGraph.children[ 0 ].animations = animations; + sceneGraph = sceneGraph.children[ 0 ]; + + } + + sceneGraph.animations = animations; + + }, + + // parse nodes in FBXTree.Objects.Model + parseModels: function ( skeletons, geometryMap, materialMap ) { + + var modelMap = new Map(); + var modelNodes = fbxTree.Objects.Model; + + for ( var nodeID in modelNodes ) { + + var id = parseInt( nodeID ); + var node = modelNodes[ nodeID ]; + var relationships = connections.get( id ); + + var model = this.buildSkeleton( relationships, skeletons, id, node.attrName ); + + if ( ! model ) { + + switch ( node.attrType ) { + + case 'Camera': + model = this.createCamera( relationships ); + break; + case 'Light': + model = this.createLight( relationships ); + break; + case 'Mesh': + model = this.createMesh( relationships, geometryMap, materialMap ); + break; + case 'NurbsCurve': + model = this.createCurve( relationships, geometryMap ); + break; + case 'LimbNode': // usually associated with a Bone, however if a Bone was not created we'll make a Group instead + case 'Null': + default: + model = new THREE.Group(); + break; + + } + + model.name = THREE.PropertyBinding.sanitizeNodeName( node.attrName ); + model.ID = id; + + } + + this.setModelTransforms( model, node ); + modelMap.set( id, model ); + + } + + return modelMap; + + }, + + buildSkeleton: function ( relationships, skeletons, id, name ) { + + var bone = null; + + relationships.parents.forEach( function ( parent ) { + + for ( var ID in skeletons ) { + + var skeleton = skeletons[ ID ]; + + skeleton.rawBones.forEach( function ( rawBone, i ) { + + if ( rawBone.ID === parent.ID ) { + + var subBone = bone; + bone = new THREE.Bone(); + bone.matrixWorld.copy( rawBone.transformLink ); + + // set name and id here - otherwise in cases where "subBone" is created it will not have a name / id + bone.name = THREE.PropertyBinding.sanitizeNodeName( name ); + bone.ID = id; + + skeleton.bones[ i ] = bone; + + // In cases where a bone is shared between multiple meshes + // duplicate the bone here and and it as a child of the first bone + if ( subBone !== null ) { + + bone.add( subBone ); + + } + + } + + } ); + + } + + } ); + + return bone; + + }, + + // create a THREE.PerspectiveCamera or THREE.OrthographicCamera + createCamera: function ( relationships ) { + + var model; + var cameraAttribute; + + relationships.children.forEach( function ( child ) { + + var attr = fbxTree.Objects.NodeAttribute[ child.ID ]; + + if ( attr !== undefined ) { + + cameraAttribute = attr; + + } + + } ); + + if ( cameraAttribute === undefined ) { + + model = new THREE.Object3D(); + + } else { + + var type = 0; + if ( cameraAttribute.CameraProjectionType !== undefined && cameraAttribute.CameraProjectionType.value === 1 ) { + + type = 1; + + } + + var nearClippingPlane = 1; + if ( cameraAttribute.NearPlane !== undefined ) { + + nearClippingPlane = cameraAttribute.NearPlane.value / 1000; + + } + + var farClippingPlane = 1000; + if ( cameraAttribute.FarPlane !== undefined ) { + + farClippingPlane = cameraAttribute.FarPlane.value / 1000; + + } + + + var width = window.innerWidth; + var height = window.innerHeight; + + if ( cameraAttribute.AspectWidth !== undefined && cameraAttribute.AspectHeight !== undefined ) { + + width = cameraAttribute.AspectWidth.value; + height = cameraAttribute.AspectHeight.value; + + } + + var aspect = width / height; + + var fov = 45; + if ( cameraAttribute.FieldOfView !== undefined ) { + + fov = cameraAttribute.FieldOfView.value; + + } + + var focalLength = cameraAttribute.FocalLength ? cameraAttribute.FocalLength.value : null; + + switch ( type ) { + + case 0: // Perspective + model = new THREE.PerspectiveCamera( fov, aspect, nearClippingPlane, farClippingPlane ); + if ( focalLength !== null ) model.setFocalLength( focalLength ); + break; + + case 1: // Orthographic + model = new THREE.OrthographicCamera( - width / 2, width / 2, height / 2, - height / 2, nearClippingPlane, farClippingPlane ); + break; + + default: + console.warn( 'THREE.FBXLoader: Unknown camera type ' + type + '.' ); + model = new THREE.Object3D(); + break; + + } + + } + + return model; + + }, + + // Create a THREE.DirectionalLight, THREE.PointLight or THREE.SpotLight + createLight: function ( relationships ) { + + var model; + var lightAttribute; + + relationships.children.forEach( function ( child ) { + + var attr = fbxTree.Objects.NodeAttribute[ child.ID ]; + + if ( attr !== undefined ) { + + lightAttribute = attr; + + } + + } ); + + if ( lightAttribute === undefined ) { + + model = new THREE.Object3D(); + + } else { + + var type; + + // LightType can be undefined for Point lights + if ( lightAttribute.LightType === undefined ) { + + type = 0; + + } else { + + type = lightAttribute.LightType.value; + + } + + var color = 0xffffff; + + if ( lightAttribute.Color !== undefined ) { + + color = new THREE.Color().fromArray( lightAttribute.Color.value ); + + } + + var intensity = ( lightAttribute.Intensity === undefined ) ? 1 : lightAttribute.Intensity.value / 100; + + // light disabled + if ( lightAttribute.CastLightOnObject !== undefined && lightAttribute.CastLightOnObject.value === 0 ) { + + intensity = 0; + + } + + var distance = 0; + if ( lightAttribute.FarAttenuationEnd !== undefined ) { + + if ( lightAttribute.EnableFarAttenuation !== undefined && lightAttribute.EnableFarAttenuation.value === 0 ) { + + distance = 0; + + } else { + + distance = lightAttribute.FarAttenuationEnd.value; + + } + + } + + // TODO: could this be calculated linearly from FarAttenuationStart to FarAttenuationEnd? + var decay = 1; + + switch ( type ) { + + case 0: // Point + model = new THREE.PointLight( color, intensity, distance, decay ); + break; + + case 1: // Directional + model = new THREE.DirectionalLight( color, intensity ); + break; + + case 2: // Spot + var angle = Math.PI / 3; + + if ( lightAttribute.InnerAngle !== undefined ) { + + angle = THREE.Math.degToRad( lightAttribute.InnerAngle.value ); + + } + + var penumbra = 0; + if ( lightAttribute.OuterAngle !== undefined ) { + + // TODO: this is not correct - FBX calculates outer and inner angle in degrees + // with OuterAngle > InnerAngle && OuterAngle <= Math.PI + // while three.js uses a penumbra between (0, 1) to attenuate the inner angle + penumbra = THREE.Math.degToRad( lightAttribute.OuterAngle.value ); + penumbra = Math.max( penumbra, 1 ); + + } + + model = new THREE.SpotLight( color, intensity, distance, angle, penumbra, decay ); + break; + + default: + console.warn( 'THREE.FBXLoader: Unknown light type ' + lightAttribute.LightType.value + ', defaulting to a THREE.PointLight.' ); + model = new THREE.PointLight( color, intensity ); + break; + + } + + if ( lightAttribute.CastShadows !== undefined && lightAttribute.CastShadows.value === 1 ) { + + model.castShadow = true; + + } + + } + + return model; + + }, + + createMesh: function ( relationships, geometryMap, materialMap ) { + + var model; + var geometry = null; + var material = null; + var materials = []; + + // get geometry and materials(s) from connections + relationships.children.forEach( function ( child ) { + + if ( geometryMap.has( child.ID ) ) { + + geometry = geometryMap.get( child.ID ); + + } + + if ( materialMap.has( child.ID ) ) { + + materials.push( materialMap.get( child.ID ) ); + + } + + } ); + + if ( materials.length > 1 ) { + + material = materials; + + } else if ( materials.length > 0 ) { + + material = materials[ 0 ]; + + } else { + + material = new THREE.MeshPhongMaterial( { color: 0xcccccc } ); + materials.push( material ); + + } + + if ( 'color' in geometry.attributes ) { + + materials.forEach( function ( material ) { + + material.vertexColors = THREE.VertexColors; + + } ); + + } + + if ( geometry.FBX_Deformer ) { + + materials.forEach( function ( material ) { + + material.skinning = true; + + } ); + + model = new THREE.SkinnedMesh( geometry, material ); + + } else { + + model = new THREE.Mesh( geometry, material ); + + } + + return model; + + }, + + createCurve: function ( relationships, geometryMap ) { + + var geometry = relationships.children.reduce( function ( geo, child ) { + + if ( geometryMap.has( child.ID ) ) geo = geometryMap.get( child.ID ); + + return geo; + + }, null ); + + // FBX does not list materials for Nurbs lines, so we'll just put our own in here. + var material = new THREE.LineBasicMaterial( { color: 0x3300ff, linewidth: 1 } ); + return new THREE.Line( geometry, material ); + + }, + + // parse the model node for transform details and apply them to the model + setModelTransforms: function ( model, modelNode ) { + + var transformData = {}; + + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = parseInt( modelNode.RotationOrder.value ); + if ( 'Lcl_Translation' in modelNode ) transformData.translation = modelNode.Lcl_Translation.value; + if ( 'RotationOffset' in modelNode ) transformData.rotationOffset = modelNode.RotationOffset.value; + if ( 'Lcl_Rotation' in modelNode ) transformData.rotation = modelNode.Lcl_Rotation.value; + if ( 'PreRotation' in modelNode ) transformData.preRotation = modelNode.PreRotation.value; + if ( 'PostRotation' in modelNode ) transformData.postRotation = modelNode.PostRotation.value; + if ( 'Lcl_Scaling' in modelNode ) transformData.scale = modelNode.Lcl_Scaling.value; + + var transform = generateTransform( transformData ); + + model.applyMatrix( transform ); + + }, + + setLookAtProperties: function ( model, modelNode ) { + + if ( 'LookAtProperty' in modelNode ) { + + var children = connections.get( model.ID ).children; + + children.forEach( function ( child ) { + + if ( child.relationship === 'LookAtProperty' ) { + + var lookAtTarget = fbxTree.Objects.Model[ child.ID ]; + + if ( 'Lcl_Translation' in lookAtTarget ) { + + var pos = lookAtTarget.Lcl_Translation.value; + + // DirectionalLight, SpotLight + if ( model.target !== undefined ) { + + model.target.position.fromArray( pos ); + sceneGraph.add( model.target ); + + } else { // Cameras and other Object3Ds + + model.lookAt( new THREE.Vector3().fromArray( pos ) ); + + } + + } + + } + + } ); + + } + + }, + + bindSkeleton: function ( skeletons, geometryMap, modelMap ) { + var bindMatrices;// = this.parsePoseNodes(); + + for ( var ID in skeletons ) { + if (bindMatrices===undefined) bindMatrices = this.parsePoseNodes(); + + var skeleton = skeletons[ ID ]; + + var parents = connections.get( parseInt( skeleton.ID ) ).parents; + + parents.forEach( function ( parent ) { + + if ( geometryMap.has( parent.ID ) ) { + + var geoID = parent.ID; + var geoRelationships = connections.get( geoID ); + + geoRelationships.parents.forEach( function ( geoConnParent ) { + + if ( modelMap.has( geoConnParent.ID ) ) { + + var model = modelMap.get( geoConnParent.ID ); + + model.bind( new THREE.Skeleton( skeleton.bones ), bindMatrices[ geoConnParent.ID ] ); + + } + + } ); + + } + + } ); + + } + + }, + + parsePoseNodes: function () { + + var bindMatrices = {}; + + if ( 'Pose' in fbxTree.Objects ) { + + var BindPoseNode = fbxTree.Objects.Pose; + + for ( var nodeID in BindPoseNode ) { + + if ( BindPoseNode[ nodeID ].attrType === 'BindPose' ) { + + var poseNodes = BindPoseNode[ nodeID ].PoseNode; + + if ( Array.isArray( poseNodes ) ) { + + poseNodes.forEach( function ( poseNode ) { + + bindMatrices[ poseNode.Node ] = new THREE.Matrix4().fromArray( poseNode.Matrix.a ); + + } ); + + } else { + + bindMatrices[ poseNodes.Node ] = new THREE.Matrix4().fromArray( poseNodes.Matrix.a ); + + } + + } + + } + + } + + return bindMatrices; + + }, + + // Parse ambient color in FBXTree.GlobalSettings - if it's not set to black (default), create an ambient light + createAmbientLight: function () { + + if ( 'GlobalSettings' in fbxTree && 'AmbientColor' in fbxTree.GlobalSettings ) { + + var ambientColor = fbxTree.GlobalSettings.AmbientColor.value; + var r = ambientColor[ 0 ]; + var g = ambientColor[ 1 ]; + var b = ambientColor[ 2 ]; + + if ( r !== 0 || g !== 0 || b !== 0 ) { + + var color = new THREE.Color( r, g, b ); + sceneGraph.add( new THREE.AmbientLight( color, 1 ) ); + + } + + } + + }, + + setupMorphMaterials: function () { + + sceneGraph.traverse( function ( child ) { + + if ( child.isMesh ) { + + if ( child.geometry.morphAttributes.position || child.geometry.morphAttributes.normal ) { + + var uuid = child.uuid; + var matUuid = child.material.uuid; + + // if a geometry has morph targets, it cannot share the material with other geometries + var sharedMat = false; + + sceneGraph.traverse( function ( child ) { + + if ( child.isMesh ) { + + if ( child.material.uuid === matUuid && child.uuid !== uuid ) sharedMat = true; + + } + + } ); + + if ( sharedMat === true ) child.material = child.material.clone(); + + child.material.morphTargets = true; + + } + + } + + } ); + + }, + + }; + + // parse Geometry data from FBXTree and return map of BufferGeometries + function GeometryParser() {} + + GeometryParser.prototype = { + + constructor: GeometryParser, + + // Parse nodes in FBXTree.Objects.Geometry + parse: function ( deformers ) { + + var geometryMap = new Map(); + + if ( 'Geometry' in fbxTree.Objects ) { + + var geoNodes = fbxTree.Objects.Geometry; + + for ( var nodeID in geoNodes ) { + + var relationships = connections.get( parseInt( nodeID ) ); + var geo = this.parseGeometry( relationships, geoNodes[ nodeID ], deformers ); + + geometryMap.set( parseInt( nodeID ), geo ); + + } + + } + + return geometryMap; + + }, + + // Parse single node in FBXTree.Objects.Geometry + parseGeometry: function ( relationships, geoNode, deformers ) { + + switch ( geoNode.attrType ) { + + case 'Mesh': + return this.parseMeshGeometry( relationships, geoNode, deformers ); + break; + + case 'NurbsCurve': + return this.parseNurbsGeometry( geoNode ); + break; + + } + + }, + + // Parse single node mesh geometry in FBXTree.Objects.Geometry + parseMeshGeometry: function ( relationships, geoNode, deformers ) { + + var skeletons = deformers.skeletons; + var morphTargets = deformers.morphTargets; + + var modelNodes = relationships.parents.map( function ( parent ) { + + return fbxTree.Objects.Model[ parent.ID ]; + + } ); + + // don't create geometry if it is not associated with any models + if ( modelNodes.length === 0 ) return; + + var skeleton = relationships.children.reduce( function ( skeleton, child ) { + + if ( skeletons[ child.ID ] !== undefined ) skeleton = skeletons[ child.ID ]; + + return skeleton; + + }, null ); + + var morphTarget = relationships.children.reduce( function ( morphTarget, child ) { + + if ( morphTargets[ child.ID ] !== undefined ) morphTarget = morphTargets[ child.ID ]; + + return morphTarget; + + }, null ); + + // TODO: if there is more than one model associated with the geometry, AND the models have + // different geometric transforms, then this will cause problems + // if ( modelNodes.length > 1 ) { } + + // For now just assume one model and get the preRotations from that + var modelNode = modelNodes[ 0 ]; + + var transformData = {}; + + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = modelNode.RotationOrder.value; + if ( 'GeometricTranslation' in modelNode ) transformData.translation = modelNode.GeometricTranslation.value; + if ( 'GeometricRotation' in modelNode ) transformData.rotation = modelNode.GeometricRotation.value; + if ( 'GeometricScaling' in modelNode ) transformData.scale = modelNode.GeometricScaling.value; + + var transform = generateTransform( transformData ); + + return this.genGeometry( geoNode, skeleton, morphTarget, transform ); + + }, + + // Generate a THREE.BufferGeometry from a node in FBXTree.Objects.Geometry + genGeometry: function ( geoNode, skeleton, morphTarget, preTransform ) { + + var geo = new THREE.BufferGeometry(); + if ( geoNode.attrName ) geo.name = geoNode.attrName; + + var geoInfo = this.parseGeoNode( geoNode, skeleton ); + var buffers = this.genBuffers( geoInfo ); + + var positionAttribute = new THREE.Float32BufferAttribute( buffers.vertex, 3 ); + + preTransform.applyToBufferAttribute( positionAttribute ); + + geo.addAttribute( 'position', positionAttribute ); + + if ( buffers.colors.length > 0 ) { + + geo.addAttribute( 'color', new THREE.Float32BufferAttribute( buffers.colors, 3 ) ); + + } + + if ( skeleton ) { + + geo.addAttribute( 'skinIndex', new THREE.Uint16BufferAttribute( buffers.weightsIndices, 4 ) ); + + geo.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( buffers.vertexWeights, 4 ) ); + + // used later to bind the skeleton to the model + geo.FBX_Deformer = skeleton; + + } + + if ( buffers.normal.length > 0 ) { + + var normalAttribute = new THREE.Float32BufferAttribute( buffers.normal, 3 ); + + var normalMatrix = new THREE.Matrix3().getNormalMatrix( preTransform ); + normalMatrix.applyToBufferAttribute( normalAttribute ); + + geo.addAttribute( 'normal', normalAttribute ); + + } + + buffers.uvs.forEach( function ( uvBuffer, i ) { + + // subsequent uv buffers are called 'uv1', 'uv2', ... + var name = 'uv' + ( i + 1 ).toString(); + + // the first uv buffer is just called 'uv' + if ( i === 0 ) { + + name = 'uv'; + + } + + geo.addAttribute( name, new THREE.Float32BufferAttribute( buffers.uvs[ i ], 2 ) ); + + } ); + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + // Convert the material indices of each vertex into rendering groups on the geometry. + var prevMaterialIndex = buffers.materialIndex[ 0 ]; + var startIndex = 0; + + buffers.materialIndex.forEach( function ( currentIndex, i ) { + + if ( currentIndex !== prevMaterialIndex ) { + + geo.addGroup( startIndex, i - startIndex, prevMaterialIndex ); + + prevMaterialIndex = currentIndex; + startIndex = i; + + } + + } ); + + // the loop above doesn't add the last group, do that here. + if ( geo.groups.length > 0 ) { + + var lastGroup = geo.groups[ geo.groups.length - 1 ]; + var lastIndex = lastGroup.start + lastGroup.count; + + if ( lastIndex !== buffers.materialIndex.length ) { + + geo.addGroup( lastIndex, buffers.materialIndex.length - lastIndex, prevMaterialIndex ); + + } + + } + + // case where there are multiple materials but the whole geometry is only + // using one of them + if ( geo.groups.length === 0 ) { + + geo.addGroup( 0, buffers.materialIndex.length, buffers.materialIndex[ 0 ] ); + + } + + } + + this.addMorphTargets( geo, geoNode, morphTarget, preTransform ); + + return geo; + + }, + + parseGeoNode: function ( geoNode, skeleton ) { + + var geoInfo = {}; + + geoInfo.vertexPositions = ( geoNode.Vertices !== undefined ) ? geoNode.Vertices.a : []; + geoInfo.vertexIndices = ( geoNode.PolygonVertexIndex !== undefined ) ? geoNode.PolygonVertexIndex.a : []; + + if ( geoNode.LayerElementColor ) { + + geoInfo.color = this.parseVertexColors( geoNode.LayerElementColor[ 0 ] ); + + } + + if ( geoNode.LayerElementMaterial ) { + + geoInfo.material = this.parseMaterialIndices( geoNode.LayerElementMaterial[ 0 ] ); + + } + + if ( geoNode.LayerElementNormal ) { + + geoInfo.normal = this.parseNormals( geoNode.LayerElementNormal[ 0 ] ); + + } + + if ( geoNode.LayerElementUV ) { + + geoInfo.uv = []; + + var i = 0; + while ( geoNode.LayerElementUV[ i ] ) { + + geoInfo.uv.push( this.parseUVs( geoNode.LayerElementUV[ i ] ) ); + i ++; + + } + + } + + geoInfo.weightTable = {}; + + if ( skeleton !== null ) { + + geoInfo.skeleton = skeleton; + + skeleton.rawBones.forEach( function ( rawBone, i ) { + + // loop over the bone's vertex indices and weights + rawBone.indices.forEach( function ( index, j ) { + + if ( geoInfo.weightTable[ index ] === undefined ) geoInfo.weightTable[ index ] = []; + + geoInfo.weightTable[ index ].push( { + + id: i, + weight: rawBone.weights[ j ], + + } ); + + } ); + + } ); + + } + + return geoInfo; + + }, + + genBuffers: function ( geoInfo ) { + + var buffers = { + vertex: [], + normal: [], + colors: [], + uvs: [], + materialIndex: [], + vertexWeights: [], + weightsIndices: [], + }; + + var polygonIndex = 0; + var faceLength = 0; + var displayedWeightsWarning = false; + + // these will hold data for a single face + var facePositionIndexes = []; + var faceNormals = []; + var faceColors = []; + var faceUVs = []; + var faceWeights = []; + var faceWeightIndices = []; + + var self = this; + geoInfo.vertexIndices.forEach( function ( vertexIndex, polygonVertexIndex ) { + + var endOfFace = false; + + // Face index and vertex index arrays are combined in a single array + // A cube with quad faces looks like this: + // PolygonVertexIndex: *24 { + // a: 0, 1, 3, -3, 2, 3, 5, -5, 4, 5, 7, -7, 6, 7, 1, -1, 1, 7, 5, -4, 6, 0, 2, -5 + // } + // Negative numbers mark the end of a face - first face here is 0, 1, 3, -3 + // to find index of last vertex bit shift the index: ^ - 1 + if ( vertexIndex < 0 ) { + + vertexIndex = vertexIndex ^ - 1; // equivalent to ( x * -1 ) - 1 + endOfFace = true; + + } + + var weightIndices = []; + var weights = []; + + facePositionIndexes.push( vertexIndex * 3, vertexIndex * 3 + 1, vertexIndex * 3 + 2 ); + + if ( geoInfo.color ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.color ); + + faceColors.push( data[ 0 ], data[ 1 ], data[ 2 ] ); + + } + + if ( geoInfo.skeleton ) { + + if ( geoInfo.weightTable[ vertexIndex ] !== undefined ) { + + geoInfo.weightTable[ vertexIndex ].forEach( function ( wt ) { + + weights.push( wt.weight ); + weightIndices.push( wt.id ); + + } ); + + + } + + if ( weights.length > 4 ) { + + if ( ! displayedWeightsWarning ) { + + console.warn( 'THREE.FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights.' ); + displayedWeightsWarning = true; + + } + + var wIndex = [ 0, 0, 0, 0 ]; + var Weight = [ 0, 0, 0, 0 ]; + + weights.forEach( function ( weight, weightIndex ) { + + var currentWeight = weight; + var currentIndex = weightIndices[ weightIndex ]; + + Weight.forEach( function ( comparedWeight, comparedWeightIndex, comparedWeightArray ) { + + if ( currentWeight > comparedWeight ) { + + comparedWeightArray[ comparedWeightIndex ] = currentWeight; + currentWeight = comparedWeight; + + var tmp = wIndex[ comparedWeightIndex ]; + wIndex[ comparedWeightIndex ] = currentIndex; + currentIndex = tmp; + + } + + } ); + + } ); + + weightIndices = wIndex; + weights = Weight; + + } + + // if the weight array is shorter than 4 pad with 0s + while ( weights.length < 4 ) { + + weights.push( 0 ); + weightIndices.push( 0 ); + + } + + for ( var i = 0; i < 4; ++ i ) { + + faceWeights.push( weights[ i ] ); + faceWeightIndices.push( weightIndices[ i ] ); + + } + + } + + if ( geoInfo.normal ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.normal ); + + faceNormals.push( data[ 0 ], data[ 1 ], data[ 2 ] ); + + } + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + var materialIndex = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.material )[ 0 ]; + + } + + if ( geoInfo.uv ) { + + geoInfo.uv.forEach( function ( uv, i ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, uv ); + + if ( faceUVs[ i ] === undefined ) { + + faceUVs[ i ] = []; + + } + + faceUVs[ i ].push( data[ 0 ] ); + faceUVs[ i ].push( data[ 1 ] ); + + } ); + + } + + faceLength ++; + + if ( endOfFace ) { + + self.genFace( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ); + + polygonIndex ++; + faceLength = 0; + + // reset arrays for the next face + facePositionIndexes = []; + faceNormals = []; + faceColors = []; + faceUVs = []; + faceWeights = []; + faceWeightIndices = []; + + } + + } ); + + return buffers; + + }, + + // Generate data for a single face in a geometry. If the face is a quad then split it into 2 tris + genFace: function ( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ) { + + for ( var i = 2; i < faceLength; i ++ ) { + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 0 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 2 ] ] ); + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 2 ] ] ); + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 2 ] ] ); + + if ( geoInfo.skeleton ) { + + buffers.vertexWeights.push( faceWeights[ 0 ] ); + buffers.vertexWeights.push( faceWeights[ 1 ] ); + buffers.vertexWeights.push( faceWeights[ 2 ] ); + buffers.vertexWeights.push( faceWeights[ 3 ] ); + + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 1 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 2 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 3 ] ); + + buffers.vertexWeights.push( faceWeights[ i * 4 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 1 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 2 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ 0 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ i * 4 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 3 ] ); + + } + + if ( geoInfo.color ) { + + buffers.colors.push( faceColors[ 0 ] ); + buffers.colors.push( faceColors[ 1 ] ); + buffers.colors.push( faceColors[ 2 ] ); + + buffers.colors.push( faceColors[ ( i - 1 ) * 3 ] ); + buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 1 ] ); + buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 2 ] ); + + buffers.colors.push( faceColors[ i * 3 ] ); + buffers.colors.push( faceColors[ i * 3 + 1 ] ); + buffers.colors.push( faceColors[ i * 3 + 2 ] ); + + } + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + buffers.materialIndex.push( materialIndex ); + buffers.materialIndex.push( materialIndex ); + buffers.materialIndex.push( materialIndex ); + + } + + if ( geoInfo.normal ) { + + buffers.normal.push( faceNormals[ 0 ] ); + buffers.normal.push( faceNormals[ 1 ] ); + buffers.normal.push( faceNormals[ 2 ] ); + + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 ] ); + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 1 ] ); + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 2 ] ); + + buffers.normal.push( faceNormals[ i * 3 ] ); + buffers.normal.push( faceNormals[ i * 3 + 1 ] ); + buffers.normal.push( faceNormals[ i * 3 + 2 ] ); + + } + + if ( geoInfo.uv ) { + + geoInfo.uv.forEach( function ( uv, j ) { + + if ( buffers.uvs[ j ] === undefined ) buffers.uvs[ j ] = []; + + buffers.uvs[ j ].push( faceUVs[ j ][ 0 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ 1 ] ); + + buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 + 1 ] ); + + buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 + 1 ] ); + + } ); + + } + + } + + }, + + addMorphTargets: function ( parentGeo, parentGeoNode, morphTarget, preTransform ) { + + if ( morphTarget === null ) return; + + parentGeo.morphAttributes.position = []; + // parentGeo.morphAttributes.normal = []; // not implemented + + var self = this; + morphTarget.rawTargets.forEach( function ( rawTarget ) { + + var morphGeoNode = fbxTree.Objects.Geometry[ rawTarget.geoID ]; + + if ( morphGeoNode !== undefined ) { + + self.genMorphGeometry( parentGeo, parentGeoNode, morphGeoNode, preTransform, rawTarget.name ); + + } + + } ); + + }, + + // a morph geometry node is similar to a standard node, and the node is also contained + // in FBXTree.Objects.Geometry, however it can only have attributes for position, normal + // and a special attribute Index defining which vertices of the original geometry are affected + // Normal and position attributes only have data for the vertices that are affected by the morph + genMorphGeometry: function ( parentGeo, parentGeoNode, morphGeoNode, preTransform, name ) { + + var morphGeo = new THREE.BufferGeometry(); + if ( morphGeoNode.attrName ) morphGeo.name = morphGeoNode.attrName; + + var vertexIndices = ( parentGeoNode.PolygonVertexIndex !== undefined ) ? parentGeoNode.PolygonVertexIndex.a : []; + + // make a copy of the parent's vertex positions + var vertexPositions = ( parentGeoNode.Vertices !== undefined ) ? parentGeoNode.Vertices.a.slice() : []; + + var morphPositions = ( morphGeoNode.Vertices !== undefined ) ? morphGeoNode.Vertices.a : []; + var indices = ( morphGeoNode.Indexes !== undefined ) ? morphGeoNode.Indexes.a : []; + + for ( var i = 0; i < indices.length; i ++ ) { + + var morphIndex = indices[ i ] * 3; + + // FBX format uses blend shapes rather than morph targets. This can be converted + // by additively combining the blend shape positions with the original geometry's positions + vertexPositions[ morphIndex ] += morphPositions[ i * 3 ]; + vertexPositions[ morphIndex + 1 ] += morphPositions[ i * 3 + 1 ]; + vertexPositions[ morphIndex + 2 ] += morphPositions[ i * 3 + 2 ]; + + } + + // TODO: add morph normal support + var morphGeoInfo = { + vertexIndices: vertexIndices, + vertexPositions: vertexPositions, + }; + + var morphBuffers = this.genBuffers( morphGeoInfo ); + + var positionAttribute = new THREE.Float32BufferAttribute( morphBuffers.vertex, 3 ); + positionAttribute.name = name || morphGeoNode.attrName; + + preTransform.applyToBufferAttribute( positionAttribute ); + + parentGeo.morphAttributes.position.push( positionAttribute ); + + }, + + // Parse normal from FBXTree.Objects.Geometry.LayerElementNormal if it exists + parseNormals: function ( NormalNode ) { + + var mappingType = NormalNode.MappingInformationType; + var referenceType = NormalNode.ReferenceInformationType; + var buffer = NormalNode.Normals.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + if ( 'NormalIndex' in NormalNode ) { + + indexBuffer = NormalNode.NormalIndex.a; + + } else if ( 'NormalsIndex' in NormalNode ) { + + indexBuffer = NormalNode.NormalsIndex.a; + + } + + } + + return { + dataSize: 3, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse UVs from FBXTree.Objects.Geometry.LayerElementUV if it exists + parseUVs: function ( UVNode ) { + + var mappingType = UVNode.MappingInformationType; + var referenceType = UVNode.ReferenceInformationType; + var buffer = UVNode.UV.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + indexBuffer = UVNode.UVIndex.a; + + } + + return { + dataSize: 2, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse Vertex Colors from FBXTree.Objects.Geometry.LayerElementColor if it exists + parseVertexColors: function ( ColorNode ) { + + var mappingType = ColorNode.MappingInformationType; + var referenceType = ColorNode.ReferenceInformationType; + var buffer = ColorNode.Colors.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + indexBuffer = ColorNode.ColorIndex.a; + + } + + return { + dataSize: 4, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse mapping and material data in FBXTree.Objects.Geometry.LayerElementMaterial if it exists + parseMaterialIndices: function ( MaterialNode ) { + + var mappingType = MaterialNode.MappingInformationType; + var referenceType = MaterialNode.ReferenceInformationType; + + if ( mappingType === 'NoMappingInformation' ) { + + return { + dataSize: 1, + buffer: [ 0 ], + indices: [ 0 ], + mappingType: 'AllSame', + referenceType: referenceType + }; + + } + + var materialIndexBuffer = MaterialNode.Materials.a; + + // Since materials are stored as indices, there's a bit of a mismatch between FBX and what + // we expect.So we create an intermediate buffer that points to the index in the buffer, + // for conforming with the other functions we've written for other data. + var materialIndices = []; + + for ( var i = 0; i < materialIndexBuffer.length; ++ i ) { + + materialIndices.push( i ); + + } + + return { + dataSize: 1, + buffer: materialIndexBuffer, + indices: materialIndices, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Generate a NurbGeometry from a node in FBXTree.Objects.Geometry + parseNurbsGeometry: function ( geoNode ) { + + if ( THREE.NURBSCurve === undefined ) { + + console.error( 'THREE.FBXLoader: The loader relies on THREE.NURBSCurve for any nurbs present in the model. Nurbs will show up as empty geometry.' ); + return new THREE.BufferGeometry(); + + } + + var order = parseInt( geoNode.Order ); + + if ( isNaN( order ) ) { + + console.error( 'THREE.FBXLoader: Invalid Order %s given for geometry ID: %s', geoNode.Order, geoNode.id ); + return new THREE.BufferGeometry(); + + } + + var degree = order - 1; + + var knots = geoNode.KnotVector.a; + var controlPoints = []; + var pointsValues = geoNode.Points.a; + + for ( var i = 0, l = pointsValues.length; i < l; i += 4 ) { + + controlPoints.push( new THREE.Vector4().fromArray( pointsValues, i ) ); + + } + + var startKnot, endKnot; + + if ( geoNode.Form === 'Closed' ) { + + controlPoints.push( controlPoints[ 0 ] ); + + } else if ( geoNode.Form === 'Periodic' ) { + + startKnot = degree; + endKnot = knots.length - 1 - startKnot; + + for ( var i = 0; i < degree; ++ i ) { + + controlPoints.push( controlPoints[ i ] ); + + } + + } + + var curve = new THREE.NURBSCurve( degree, knots, controlPoints, startKnot, endKnot ); + var vertices = curve.getPoints( controlPoints.length * 7 ); + + var positions = new Float32Array( vertices.length * 3 ); + + vertices.forEach( function ( vertex, i ) { + + vertex.toArray( positions, i * 3 ); + + } ); + + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + + return geometry; + + }, + + }; + + // parse animation data from FBXTree + function AnimationParser() {} + + AnimationParser.prototype = { + + constructor: AnimationParser, + + // take raw animation clips and turn them into three.js animation clips + parse: function () { + + var animationClips = []; + + + var rawClips = this.parseClips(); + + if ( rawClips === undefined ) return; + + for ( var key in rawClips ) { + + var rawClip = rawClips[ key ]; + + var clip = this.addClip( rawClip ); + + animationClips.push( clip ); + + } + + return animationClips; + + }, + + parseClips: function () { + + // since the actual transformation data is stored in FBXTree.Objects.AnimationCurve, + // if this is undefined we can safely assume there are no animations + if ( fbxTree.Objects.AnimationCurve === undefined ) return undefined; + + var curveNodesMap = this.parseAnimationCurveNodes(); + + this.parseAnimationCurves( curveNodesMap ); + + var layersMap = this.parseAnimationLayers( curveNodesMap ); + var rawClips = this.parseAnimStacks( layersMap ); + + return rawClips; + + }, + + // parse nodes in FBXTree.Objects.AnimationCurveNode + // each AnimationCurveNode holds data for an animation transform for a model (e.g. left arm rotation ) + // and is referenced by an AnimationLayer + parseAnimationCurveNodes: function () { + + var rawCurveNodes = fbxTree.Objects.AnimationCurveNode; + + var curveNodesMap = new Map(); + + for ( var nodeID in rawCurveNodes ) { + + var rawCurveNode = rawCurveNodes[ nodeID ]; + + if ( rawCurveNode.attrName.match( /S|R|T|DeformPercent/ ) !== null ) { + + var curveNode = { + + id: rawCurveNode.id, + attr: rawCurveNode.attrName, + curves: {}, + + }; + + curveNodesMap.set( curveNode.id, curveNode ); + + } + + } + + return curveNodesMap; + + }, + + // parse nodes in FBXTree.Objects.AnimationCurve and connect them up to + // previously parsed AnimationCurveNodes. Each AnimationCurve holds data for a single animated + // axis ( e.g. times and values of x rotation) + parseAnimationCurves: function ( curveNodesMap ) { + + var rawCurves = fbxTree.Objects.AnimationCurve; + + // TODO: Many values are identical up to roundoff error, but won't be optimised + // e.g. position times: [0, 0.4, 0. 8] + // position values: [7.23538335023477e-7, 93.67518615722656, -0.9982695579528809, 7.23538335023477e-7, 93.67518615722656, -0.9982695579528809, 7.235384487103147e-7, 93.67520904541016, -0.9982695579528809] + // clearly, this should be optimised to + // times: [0], positions [7.23538335023477e-7, 93.67518615722656, -0.9982695579528809] + // this shows up in nearly every FBX file, and generally time array is length > 100 + + for ( var nodeID in rawCurves ) { + + var animationCurve = { + + id: rawCurves[ nodeID ].id, + times: rawCurves[ nodeID ].KeyTime.a.map( convertFBXTimeToSeconds ), + values: rawCurves[ nodeID ].KeyValueFloat.a, + + }; + + var relationships = connections.get( animationCurve.id ); + + if ( relationships !== undefined ) { + + var animationCurveID = relationships.parents[ 0 ].ID; + var animationCurveRelationship = relationships.parents[ 0 ].relationship; + + if ( animationCurveRelationship.match( /X/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'x' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /Y/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'y' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /Z/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'z' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /d|DeformPercent/ ) && curveNodesMap.has( animationCurveID ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'morph' ] = animationCurve; + + } + + } + + } + + }, + + // parse nodes in FBXTree.Objects.AnimationLayer. Each layers holds references + // to various AnimationCurveNodes and is referenced by an AnimationStack node + // note: theoretically a stack can have multiple layers, however in practice there always seems to be one per stack + parseAnimationLayers: function ( curveNodesMap ) { + + var rawLayers = fbxTree.Objects.AnimationLayer; + + var layersMap = new Map(); + + for ( var nodeID in rawLayers ) { + + var layerCurveNodes = []; + + var connection = connections.get( parseInt( nodeID ) ); + + if ( connection !== undefined ) { + + // all the animationCurveNodes used in the layer + var children = connection.children; + + var self = this; + children.forEach( function ( child, i ) { + + if ( curveNodesMap.has( child.ID ) ) { + + var curveNode = curveNodesMap.get( child.ID ); + + // check that the curves are defined for at least one axis, otherwise ignore the curveNode + if ( curveNode.curves.x !== undefined || curveNode.curves.y !== undefined || curveNode.curves.z !== undefined ) { + + if ( layerCurveNodes[ i ] === undefined ) { + + var modelID; + + connections.get( child.ID ).parents.forEach( function ( parent ) { + + if ( parent.relationship !== undefined ) modelID = parent.ID; + + } ); + + var rawModel = fbxTree.Objects.Model[ modelID.toString() ]; + + var node = { + + modelName: THREE.PropertyBinding.sanitizeNodeName( rawModel.attrName ), + initialPosition: [ 0, 0, 0 ], + initialRotation: [ 0, 0, 0 ], + initialScale: [ 1, 1, 1 ], + transform: self.getModelAnimTransform( rawModel ), + + }; + + // if the animated model is pre rotated, we'll have to apply the pre rotations to every + // animation value as well + if ( 'PreRotation' in rawModel ) node.preRotations = rawModel.PreRotation.value; + if ( 'PostRotation' in rawModel ) node.postRotations = rawModel.PostRotation.value; + + layerCurveNodes[ i ] = node; + + } + + layerCurveNodes[ i ][ curveNode.attr ] = curveNode; + + } else if ( curveNode.curves.morph !== undefined ) { + + if ( layerCurveNodes[ i ] === undefined ) { + + var deformerID; + + connections.get( child.ID ).parents.forEach( function ( parent ) { + + if ( parent.relationship !== undefined ) deformerID = parent.ID; + + } ); + + var morpherID = connections.get( deformerID ).parents[ 0 ].ID; + var geoID = connections.get( morpherID ).parents[ 0 ].ID; + + // assuming geometry is not used in more than one model + var modelID = connections.get( geoID ).parents[ 0 ].ID; + + var rawModel = fbxTree.Objects.Model[ modelID ]; + + var node = { + + modelName: THREE.PropertyBinding.sanitizeNodeName( rawModel.attrName ), + morphName: fbxTree.Objects.Deformer[ deformerID ].attrName, + + }; + + layerCurveNodes[ i ] = node; + + } + + layerCurveNodes[ i ][ curveNode.attr ] = curveNode; + + } + + } + + } ); + + layersMap.set( parseInt( nodeID ), layerCurveNodes ); + + } + + } + + return layersMap; + + }, + + getModelAnimTransform: function ( modelNode ) { + + var transformData = {}; + + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = parseInt( modelNode.RotationOrder.value ); + + if ( 'Lcl_Translation' in modelNode ) transformData.translation = modelNode.Lcl_Translation.value; + if ( 'RotationOffset' in modelNode ) transformData.rotationOffset = modelNode.RotationOffset.value; + + if ( 'Lcl_Rotation' in modelNode ) transformData.rotation = modelNode.Lcl_Rotation.value; + if ( 'PreRotation' in modelNode ) transformData.preRotation = modelNode.PreRotation.value; + + if ( 'PostRotation' in modelNode ) transformData.postRotation = modelNode.PostRotation.value; + + if ( 'Lcl_Scaling' in modelNode ) transformData.scale = modelNode.Lcl_Scaling.value; + + return generateTransform( transformData ); + + }, + + // parse nodes in FBXTree.Objects.AnimationStack. These are the top level node in the animation + // hierarchy. Each Stack node will be used to create a THREE.AnimationClip + parseAnimStacks: function ( layersMap ) { + + var rawStacks = fbxTree.Objects.AnimationStack; + + // connect the stacks (clips) up to the layers + var rawClips = {}; + + for ( var nodeID in rawStacks ) { + + var children = connections.get( parseInt( nodeID ) ).children; + + if ( children.length > 1 ) { + + // it seems like stacks will always be associated with a single layer. But just in case there are files + // where there are multiple layers per stack, we'll display a warning + console.warn( 'THREE.FBXLoader: Encountered an animation stack with multiple layers, this is currently not supported. Ignoring subsequent layers.' ); + + } + + var layer = layersMap.get( children[ 0 ].ID ); + + rawClips[ nodeID ] = { + + name: rawStacks[ nodeID ].attrName, + layer: layer, + + }; + + } + + return rawClips; + + }, + + addClip: function ( rawClip ) { + + var tracks = []; + + var self = this; + rawClip.layer.forEach( function ( rawTracks ) { + + tracks = tracks.concat( self.generateTracks( rawTracks ) ); + + } ); + + return new THREE.AnimationClip( rawClip.name, - 1, tracks ); + + }, + + generateTracks: function ( rawTracks ) { + + var tracks = []; + + var initialPosition = new THREE.Vector3(); + var initialRotation = new THREE.Quaternion(); + var initialScale = new THREE.Vector3(); + + if ( rawTracks.transform ) rawTracks.transform.decompose( initialPosition, initialRotation, initialScale ); + + initialPosition = initialPosition.toArray(); + initialRotation = new THREE.Euler().setFromQuaternion( initialRotation ).toArray(); // todo: euler order + initialScale = initialScale.toArray(); + + if ( rawTracks.T !== undefined && Object.keys( rawTracks.T.curves ).length > 0 ) { + + var positionTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.T.curves, initialPosition, 'position' ); + if ( positionTrack !== undefined ) tracks.push( positionTrack ); + + } + + if ( rawTracks.R !== undefined && Object.keys( rawTracks.R.curves ).length > 0 ) { + + var rotationTrack = this.generateRotationTrack( rawTracks.modelName, rawTracks.R.curves, initialRotation, rawTracks.preRotations, rawTracks.postRotations ); + if ( rotationTrack !== undefined ) tracks.push( rotationTrack ); + + } + + if ( rawTracks.S !== undefined && Object.keys( rawTracks.S.curves ).length > 0 ) { + + var scaleTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.S.curves, initialScale, 'scale' ); + if ( scaleTrack !== undefined ) tracks.push( scaleTrack ); + + } + + if ( rawTracks.DeformPercent !== undefined ) { + + var morphTrack = this.generateMorphTrack( rawTracks ); + if ( morphTrack !== undefined ) tracks.push( morphTrack ); + + } + + return tracks; + + }, + + generateVectorTrack: function ( modelName, curves, initialValue, type ) { + + var times = this.getTimesForAllAxes( curves ); + var values = this.getKeyframeTrackValues( times, curves, initialValue ); + + return new THREE.VectorKeyframeTrack( modelName + '.' + type, times, values ); + + }, + + generateRotationTrack: function ( modelName, curves, initialValue, preRotations, postRotations ) { + + if ( curves.x !== undefined ) { + + this.interpolateRotations( curves.x ); + curves.x.values = curves.x.values.map( THREE.Math.degToRad ); + + } + if ( curves.y !== undefined ) { + + this.interpolateRotations( curves.y ); + curves.y.values = curves.y.values.map( THREE.Math.degToRad ); + + } + if ( curves.z !== undefined ) { + + this.interpolateRotations( curves.z ); + curves.z.values = curves.z.values.map( THREE.Math.degToRad ); + + } + + var times = this.getTimesForAllAxes( curves ); + var values = this.getKeyframeTrackValues( times, curves, initialValue ); + + if ( preRotations !== undefined ) { + + preRotations = preRotations.map( THREE.Math.degToRad ); + preRotations.push( 'ZYX' ); + + preRotations = new THREE.Euler().fromArray( preRotations ); + preRotations = new THREE.Quaternion().setFromEuler( preRotations ); + + } + + if ( postRotations !== undefined ) { + + postRotations = postRotations.map( THREE.Math.degToRad ); + postRotations.push( 'ZYX' ); + + postRotations = new THREE.Euler().fromArray( postRotations ); + postRotations = new THREE.Quaternion().setFromEuler( postRotations ).inverse(); + + } + + var quaternion = new THREE.Quaternion(); + var euler = new THREE.Euler(); + + var quaternionValues = []; + + for ( var i = 0; i < values.length; i += 3 ) { + + euler.set( values[ i ], values[ i + 1 ], values[ i + 2 ], 'ZYX' ); + + quaternion.setFromEuler( euler ); + + if ( preRotations !== undefined ) quaternion.premultiply( preRotations ); + if ( postRotations !== undefined ) quaternion.multiply( postRotations ); + + quaternion.toArray( quaternionValues, ( i / 3 ) * 4 ); + + } + + return new THREE.QuaternionKeyframeTrack( modelName + '.quaternion', times, quaternionValues ); + + }, + + generateMorphTrack: function ( rawTracks ) { + + var curves = rawTracks.DeformPercent.curves.morph; + var values = curves.values.map( function ( val ) { + + return val / 100; + + } ); + + var morphNum = sceneGraph.getObjectByName( rawTracks.modelName ).morphTargetDictionary[ rawTracks.morphName ]; + + return new THREE.NumberKeyframeTrack( rawTracks.modelName + '.morphTargetInfluences[' + morphNum + ']', curves.times, values ); + + }, + + // For all animated objects, times are defined separately for each axis + // Here we'll combine the times into one sorted array without duplicates + getTimesForAllAxes: function ( curves ) { + + var times = []; + + // first join together the times for each axis, if defined + if ( curves.x !== undefined ) times = times.concat( curves.x.times ); + if ( curves.y !== undefined ) times = times.concat( curves.y.times ); + if ( curves.z !== undefined ) times = times.concat( curves.z.times ); + + // then sort them and remove duplicates + times = times.sort( function ( a, b ) { + + return a - b; + + } ).filter( function ( elem, index, array ) { + + return array.indexOf( elem ) == index; + + } ); + + return times; + + }, + + getKeyframeTrackValues: function ( times, curves, initialValue ) { + + var prevValue = initialValue; + + var values = []; + + var xIndex = - 1; + var yIndex = - 1; + var zIndex = - 1; + + times.forEach( function ( time ) { + + if ( curves.x ) xIndex = curves.x.times.indexOf( time ); + if ( curves.y ) yIndex = curves.y.times.indexOf( time ); + if ( curves.z ) zIndex = curves.z.times.indexOf( time ); + + // if there is an x value defined for this frame, use that + if ( xIndex !== - 1 ) { + + var xValue = curves.x.values[ xIndex ]; + values.push( xValue ); + prevValue[ 0 ] = xValue; + + } else { + + // otherwise use the x value from the previous frame + values.push( prevValue[ 0 ] ); + + } + + if ( yIndex !== - 1 ) { + + var yValue = curves.y.values[ yIndex ]; + values.push( yValue ); + prevValue[ 1 ] = yValue; + + } else { + + values.push( prevValue[ 1 ] ); + + } + + if ( zIndex !== - 1 ) { + + var zValue = curves.z.values[ zIndex ]; + values.push( zValue ); + prevValue[ 2 ] = zValue; + + } else { + + values.push( prevValue[ 2 ] ); + + } + + } ); + + return values; + + }, + + // Rotations are defined as Euler angles which can have values of any size + // These will be converted to quaternions which don't support values greater than + // PI, so we'll interpolate large rotations + interpolateRotations: function ( curve ) { + + for ( var i = 1; i < curve.values.length; i ++ ) { + + var initialValue = curve.values[ i - 1 ]; + var valuesSpan = curve.values[ i ] - initialValue; + + var absoluteSpan = Math.abs( valuesSpan ); + + if ( absoluteSpan >= 180 ) { + + var numSubIntervals = absoluteSpan / 180; + + var step = valuesSpan / numSubIntervals; + var nextValue = initialValue + step; + + var initialTime = curve.times[ i - 1 ]; + var timeSpan = curve.times[ i ] - initialTime; + var interval = timeSpan / numSubIntervals; + var nextTime = initialTime + interval; + + var interpolatedTimes = []; + var interpolatedValues = []; + + while ( nextTime < curve.times[ i ] ) { + + interpolatedTimes.push( nextTime ); + nextTime += interval; + + interpolatedValues.push( nextValue ); + nextValue += step; + + } + + curve.times = inject( curve.times, i, interpolatedTimes ); + curve.values = inject( curve.values, i, interpolatedValues ); + + } + + } + + }, + + }; + + // parse an FBX file in ASCII format + function TextParser() {} + + TextParser.prototype = { + + constructor: TextParser, + + getPrevNode: function () { + + return this.nodeStack[ this.currentIndent - 2 ]; + + }, + + getCurrentNode: function () { + + return this.nodeStack[ this.currentIndent - 1 ]; + + }, + + getCurrentProp: function () { + + return this.currentProp; + + }, + + pushStack: function ( node ) { + + this.nodeStack.push( node ); + this.currentIndent += 1; + + }, + + popStack: function () { + + this.nodeStack.pop(); + this.currentIndent -= 1; + + }, + + setCurrentProp: function ( val, name ) { + + this.currentProp = val; + this.currentPropName = name; + + }, + + parse: function ( text ) { + + this.currentIndent = 0; + + this.allNodes = new FBXTree(); + this.nodeStack = []; + this.currentProp = []; + this.currentPropName = ''; + + var self = this; + + var split = text.split( /[\r\n]+/ ); + + split.forEach( function ( line, i ) { + + var matchComment = line.match( /^[\s\t]*;/ ); + var matchEmpty = line.match( /^[\s\t]*$/ ); + + if ( matchComment || matchEmpty ) return; + + var matchBeginning = line.match( '^\\t{' + self.currentIndent + '}(\\w+):(.*){', '' ); + var matchProperty = line.match( '^\\t{' + ( self.currentIndent ) + '}(\\w+):[\\s\\t\\r\\n](.*)' ); + var matchEnd = line.match( '^\\t{' + ( self.currentIndent - 1 ) + '}}' ); + + if ( matchBeginning ) { + + self.parseNodeBegin( line, matchBeginning ); + + } else if ( matchProperty ) { + + self.parseNodeProperty( line, matchProperty, split[ ++ i ] ); + + } else if ( matchEnd ) { + + self.popStack(); + + } else if ( line.match( /^[^\s\t}]/ ) ) { + + // large arrays are split over multiple lines terminated with a ',' character + // if this is encountered the line needs to be joined to the previous line + self.parseNodePropertyContinued( line ); + + } + + } ); + + return this.allNodes; + + }, + + parseNodeBegin: function ( line, property ) { + + var nodeName = property[ 1 ].trim().replace( /^"/, '' ).replace( /"$/, '' ); + + var nodeAttrs = property[ 2 ].split( ',' ).map( function ( attr ) { + + return attr.trim().replace( /^"/, '' ).replace( /"$/, '' ); + + } ); + + var node = { name: nodeName }; + var attrs = this.parseNodeAttr( nodeAttrs ); + + var currentNode = this.getCurrentNode(); + + // a top node + if ( this.currentIndent === 0 ) { + + this.allNodes.add( nodeName, node ); + + } else { // a subnode + + // if the subnode already exists, append it + if ( nodeName in currentNode ) { + + // special case Pose needs PoseNodes as an array + if ( nodeName === 'PoseNode' ) { + + currentNode.PoseNode.push( node ); + + } else if ( currentNode[ nodeName ].id !== undefined ) { + + currentNode[ nodeName ] = {}; + currentNode[ nodeName ][ currentNode[ nodeName ].id ] = currentNode[ nodeName ]; + + } + + if ( attrs.id !== '' ) currentNode[ nodeName ][ attrs.id ] = node; + + } else if ( typeof attrs.id === 'number' ) { + + currentNode[ nodeName ] = {}; + currentNode[ nodeName ][ attrs.id ] = node; + + } else if ( nodeName !== 'Properties70' ) { + + if ( nodeName === 'PoseNode' ) currentNode[ nodeName ] = [ node ]; + else currentNode[ nodeName ] = node; + + } + + } + + if ( typeof attrs.id === 'number' ) node.id = attrs.id; + if ( attrs.name !== '' ) node.attrName = attrs.name; + if ( attrs.type !== '' ) node.attrType = attrs.type; + + this.pushStack( node ); + + }, + + parseNodeAttr: function ( attrs ) { + + var id = attrs[ 0 ]; + + if ( attrs[ 0 ] !== '' ) { + + id = parseInt( attrs[ 0 ] ); + + if ( isNaN( id ) ) { + + id = attrs[ 0 ]; + + } + + } + + var name = '', type = ''; + + if ( attrs.length > 1 ) { + + name = attrs[ 1 ].replace( /^(\w+)::/, '' ); + type = attrs[ 2 ]; + + } + + return { id: id, name: name, type: type }; + + }, + + parseNodeProperty: function ( line, property, contentLine ) { + + var propName = property[ 1 ].replace( /^"/, '' ).replace( /"$/, '' ).trim(); + var propValue = property[ 2 ].replace( /^"/, '' ).replace( /"$/, '' ).trim(); + + // for special case: base64 image data follows "Content: ," line + // Content: , + // "/9j/4RDaRXhpZgAATU0A..." + if ( propName === 'Content' && propValue === ',' ) { + + propValue = contentLine.replace( /"/g, '' ).replace( /,$/, '' ).trim(); + + } + + var currentNode = this.getCurrentNode(); + var parentName = currentNode.name; + + if ( parentName === 'Properties70' ) { + + this.parseNodeSpecialProperty( line, propName, propValue ); + return; + + } + + // Connections + if ( propName === 'C' ) { + + var connProps = propValue.split( ',' ).slice( 1 ); + var from = parseInt( connProps[ 0 ] ); + var to = parseInt( connProps[ 1 ] ); + + var rest = propValue.split( ',' ).slice( 3 ); + + rest = rest.map( function ( elem ) { + + return elem.trim().replace( /^"/, '' ); + + } ); + + propName = 'connections'; + propValue = [ from, to ]; + append( propValue, rest ); + + if ( currentNode[ propName ] === undefined ) { + + currentNode[ propName ] = []; + + } + + } + + // Node + if ( propName === 'Node' ) currentNode.id = propValue; + + // connections + if ( propName in currentNode && Array.isArray( currentNode[ propName ] ) ) { + + currentNode[ propName ].push( propValue ); + + } else { + + if ( propName !== 'a' ) currentNode[ propName ] = propValue; + else currentNode.a = propValue; + + } + + this.setCurrentProp( currentNode, propName ); + + // convert string to array, unless it ends in ',' in which case more will be added to it + if ( propName === 'a' && propValue.slice( - 1 ) !== ',' ) { + + currentNode.a = parseNumberArray( propValue ); + + } + + }, + + parseNodePropertyContinued: function ( line ) { + + var currentNode = this.getCurrentNode(); + + currentNode.a += line; + + // if the line doesn't end in ',' we have reached the end of the property value + // so convert the string to an array + if ( line.slice( - 1 ) !== ',' ) { + + currentNode.a = parseNumberArray( currentNode.a ); + + } + + }, + + // parse "Property70" + parseNodeSpecialProperty: function ( line, propName, propValue ) { + + // split this + // P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1 + // into array like below + // ["Lcl Scaling", "Lcl Scaling", "", "A", "1,1,1" ] + var props = propValue.split( '",' ).map( function ( prop ) { + + return prop.trim().replace( /^\"/, '' ).replace( /\s/, '_' ); + + } ); + + var innerPropName = props[ 0 ]; + var innerPropType1 = props[ 1 ]; + var innerPropType2 = props[ 2 ]; + var innerPropFlag = props[ 3 ]; + var innerPropValue = props[ 4 ]; + + // cast values where needed, otherwise leave as strings + switch ( innerPropType1 ) { + + case 'int': + case 'enum': + case 'bool': + case 'ULongLong': + case 'double': + case 'Number': + case 'FieldOfView': + innerPropValue = parseFloat( innerPropValue ); + break; + + case 'Color': + case 'ColorRGB': + case 'Vector3D': + case 'Lcl_Translation': + case 'Lcl_Rotation': + case 'Lcl_Scaling': + innerPropValue = parseNumberArray( innerPropValue ); + break; + + } + + // CAUTION: these props must append to parent's parent + this.getPrevNode()[ innerPropName ] = { + + 'type': innerPropType1, + 'type2': innerPropType2, + 'flag': innerPropFlag, + 'value': innerPropValue + + }; + + this.setCurrentProp( this.getPrevNode(), innerPropName ); + + }, + + }; + + // Parse an FBX file in Binary format + function BinaryParser() {} + + BinaryParser.prototype = { + + constructor: BinaryParser, + + parse: function ( buffer ) { + + var reader = new BinaryReader( buffer ); + reader.skip( 23 ); // skip magic 23 bytes + + var version = reader.getUint32(); + + console.log( 'THREE.FBXLoader: FBX binary version: ' + version ); + + var allNodes = new FBXTree(); + + while ( ! this.endOfContent( reader ) ) { + + var node = this.parseNode( reader, version ); + if ( node !== null ) allNodes.add( node.name, node ); + + } + + return allNodes; + + }, + + // Check if reader has reached the end of content. + endOfContent: function ( reader ) { + + // footer size: 160bytes + 16-byte alignment padding + // - 16bytes: magic + // - padding til 16-byte alignment (at least 1byte?) + // (seems like some exporters embed fixed 15 or 16bytes?) + // - 4bytes: magic + // - 4bytes: version + // - 120bytes: zero + // - 16bytes: magic + if ( reader.size() % 16 === 0 ) { + + return ( ( reader.getOffset() + 160 + 16 ) & ~ 0xf ) >= reader.size(); + + } else { + + return reader.getOffset() + 160 + 16 >= reader.size(); + + } + + }, + + // recursively parse nodes until the end of the file is reached + parseNode: function ( reader, version ) { + + var node = {}; + + // The first three data sizes depends on version. + var endOffset = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + var numProperties = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + + // note: do not remove this even if you get a linter warning as it moves the buffer forward + var propertyListLen = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + + var nameLen = reader.getUint8(); + var name = reader.getString( nameLen ); + + // Regards this node as NULL-record if endOffset is zero + if ( endOffset === 0 ) return null; + + var propertyList = []; + + for ( var i = 0; i < numProperties; i ++ ) { + + propertyList.push( this.parseProperty( reader ) ); + + } + + // Regards the first three elements in propertyList as id, attrName, and attrType + var id = propertyList.length > 0 ? propertyList[ 0 ] : ''; + var attrName = propertyList.length > 1 ? propertyList[ 1 ] : ''; + var attrType = propertyList.length > 2 ? propertyList[ 2 ] : ''; + + // check if this node represents just a single property + // like (name, 0) set or (name2, [0, 1, 2]) set of {name: 0, name2: [0, 1, 2]} + node.singleProperty = ( numProperties === 1 && reader.getOffset() === endOffset ) ? true : false; + + while ( endOffset > reader.getOffset() ) { + + var subNode = this.parseNode( reader, version ); + + if ( subNode !== null ) this.parseSubNode( name, node, subNode ); + + } + + node.propertyList = propertyList; // raw property list used by parent + + if ( typeof id === 'number' ) node.id = id; + if ( attrName !== '' ) node.attrName = attrName; + if ( attrType !== '' ) node.attrType = attrType; + if ( name !== '' ) node.name = name; + + return node; + + }, + + parseSubNode: function ( name, node, subNode ) { + + // special case: child node is single property + if ( subNode.singleProperty === true ) { + + var value = subNode.propertyList[ 0 ]; + + if ( Array.isArray( value ) ) { + + node[ subNode.name ] = subNode; + + subNode.a = value; + + } else { + + node[ subNode.name ] = value; + + } + + } else if ( name === 'Connections' && subNode.name === 'C' ) { + + var array = []; + + subNode.propertyList.forEach( function ( property, i ) { + + // first Connection is FBX type (OO, OP, etc.). We'll discard these + if ( i !== 0 ) array.push( property ); + + } ); + + if ( node.connections === undefined ) { + + node.connections = []; + + } + + node.connections.push( array ); + + } else if ( subNode.name === 'Properties70' ) { + + var keys = Object.keys( subNode ); + + keys.forEach( function ( key ) { + + node[ key ] = subNode[ key ]; + + } ); + + } else if ( name === 'Properties70' && subNode.name === 'P' ) { + + var innerPropName = subNode.propertyList[ 0 ]; + var innerPropType1 = subNode.propertyList[ 1 ]; + var innerPropType2 = subNode.propertyList[ 2 ]; + var innerPropFlag = subNode.propertyList[ 3 ]; + var innerPropValue; + + if ( innerPropName.indexOf( 'Lcl ' ) === 0 ) innerPropName = innerPropName.replace( 'Lcl ', 'Lcl_' ); + if ( innerPropType1.indexOf( 'Lcl ' ) === 0 ) innerPropType1 = innerPropType1.replace( 'Lcl ', 'Lcl_' ); + + if ( innerPropType1 === 'Color' || innerPropType1 === 'ColorRGB' || innerPropType1 === 'Vector' || innerPropType1 === 'Vector3D' || innerPropType1.indexOf( 'Lcl_' ) === 0 ) { + + innerPropValue = [ + subNode.propertyList[ 4 ], + subNode.propertyList[ 5 ], + subNode.propertyList[ 6 ] + ]; + + } else { + + innerPropValue = subNode.propertyList[ 4 ]; + + } + + // this will be copied to parent, see above + node[ innerPropName ] = { + + 'type': innerPropType1, + 'type2': innerPropType2, + 'flag': innerPropFlag, + 'value': innerPropValue + + }; + + } else if ( node[ subNode.name ] === undefined ) { + + if ( typeof subNode.id === 'number' ) { + + node[ subNode.name ] = {}; + node[ subNode.name ][ subNode.id ] = subNode; + + } else { + + node[ subNode.name ] = subNode; + + } + + } else { + + if ( subNode.name === 'PoseNode' ) { + + if ( ! Array.isArray( node[ subNode.name ] ) ) { + + node[ subNode.name ] = [ node[ subNode.name ] ]; + + } + + node[ subNode.name ].push( subNode ); + + } else if ( node[ subNode.name ][ subNode.id ] === undefined ) { + + node[ subNode.name ][ subNode.id ] = subNode; + + } + + } + + }, + + parseProperty: function ( reader ) { + + var type = reader.getString( 1 ); + + switch ( type ) { + + case 'C': + return reader.getBoolean(); + + case 'D': + return reader.getFloat64(); + + case 'F': + return reader.getFloat32(); + + case 'I': + return reader.getInt32(); + + case 'L': + return reader.getInt64(); + + case 'R': + var length = reader.getUint32(); + return reader.getArrayBuffer( length ); + + case 'S': + var length = reader.getUint32(); + return reader.getString( length ); + + case 'Y': + return reader.getInt16(); + + case 'b': + case 'c': + case 'd': + case 'f': + case 'i': + case 'l': + + var arrayLength = reader.getUint32(); + var encoding = reader.getUint32(); // 0: non-compressed, 1: compressed + var compressedLength = reader.getUint32(); + + if ( encoding === 0 ) { + + switch ( type ) { + + case 'b': + case 'c': + return reader.getBooleanArray( arrayLength ); + + case 'd': + return reader.getFloat64Array( arrayLength ); + + case 'f': + return reader.getFloat32Array( arrayLength ); + + case 'i': + return reader.getInt32Array( arrayLength ); + + case 'l': + return reader.getInt64Array( arrayLength ); + + } + + } + + if ( typeof Zlib === 'undefined' ) { + + console.error( 'THREE.FBXLoader: External library Inflate.min.js required, obtain or import from https://github.com/imaya/zlib.js' ); + + } + + var inflate = new Zlib.Inflate( new Uint8Array( reader.getArrayBuffer( compressedLength ) ) ); // eslint-disable-line no-undef + var reader2 = new BinaryReader( inflate.decompress().buffer ); + + switch ( type ) { + + case 'b': + case 'c': + return reader2.getBooleanArray( arrayLength ); + + case 'd': + return reader2.getFloat64Array( arrayLength ); + + case 'f': + return reader2.getFloat32Array( arrayLength ); + + case 'i': + return reader2.getInt32Array( arrayLength ); + + case 'l': + return reader2.getInt64Array( arrayLength ); + + } + + default: + throw new Error( 'THREE.FBXLoader: Unknown property type ' + type ); + + } + + } + + }; + + function BinaryReader( buffer, littleEndian ) { + + this.dv = new DataView( buffer ); + this.offset = 0; + this.littleEndian = ( littleEndian !== undefined ) ? littleEndian : true; + + } + + BinaryReader.prototype = { + + constructor: BinaryReader, + + getOffset: function () { + + return this.offset; + + }, + + size: function () { + + return this.dv.buffer.byteLength; + + }, + + skip: function ( length ) { + + this.offset += length; + + }, + + // seems like true/false representation depends on exporter. + // true: 1 or 'Y'(=0x59), false: 0 or 'T'(=0x54) + // then sees LSB. + getBoolean: function () { + + return ( this.getUint8() & 1 ) === 1; + + }, + + getBooleanArray: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getBoolean() ); + + } + + return a; + + }, + + getUint8: function () { + + var value = this.dv.getUint8( this.offset ); + this.offset += 1; + return value; + + }, + + getInt16: function () { + + var value = this.dv.getInt16( this.offset, this.littleEndian ); + this.offset += 2; + return value; + + }, + + getInt32: function () { + + var value = this.dv.getInt32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getInt32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt32() ); + + } + + return a; + + }, + + getUint32: function () { + + var value = this.dv.getUint32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + // JavaScript doesn't support 64-bit integer so calculate this here + // 1 << 32 will return 1 so using multiply operation instead here. + // There's a possibility that this method returns wrong value if the value + // is out of the range between Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER. + // TODO: safely handle 64-bit integer + getInt64: function () { + + var low, high; + + if ( this.littleEndian ) { + + low = this.getUint32(); + high = this.getUint32(); + + } else { + + high = this.getUint32(); + low = this.getUint32(); + + } + + // calculate negative value + if ( high & 0x80000000 ) { + + high = ~ high & 0xFFFFFFFF; + low = ~ low & 0xFFFFFFFF; + + if ( low === 0xFFFFFFFF ) high = ( high + 1 ) & 0xFFFFFFFF; + + low = ( low + 1 ) & 0xFFFFFFFF; + + return - ( high * 0x100000000 + low ); + + } + + return high * 0x100000000 + low; + + }, + + getInt64Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt64() ); + + } + + return a; + + }, + + // Note: see getInt64() comment + getUint64: function () { + + var low, high; + + if ( this.littleEndian ) { + + low = this.getUint32(); + high = this.getUint32(); + + } else { + + high = this.getUint32(); + low = this.getUint32(); + + } + + return high * 0x100000000 + low; + + }, + + getFloat32: function () { + + var value = this.dv.getFloat32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getFloat32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getFloat32() ); + + } + + return a; + + }, + + getFloat64: function () { + + var value = this.dv.getFloat64( this.offset, this.littleEndian ); + this.offset += 8; + return value; + + }, + + getFloat64Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getFloat64() ); + + } + + return a; + + }, + + getArrayBuffer: function ( size ) { + + var value = this.dv.buffer.slice( this.offset, this.offset + size ); + this.offset += size; + return value; + + }, + + getString: function ( size ) { + + // note: safari 9 doesn't support Uint8Array.indexOf; create intermediate array instead + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a[ i ] = this.getUint8(); + + } + + var nullByte = a.indexOf( 0 ); + if ( nullByte >= 0 ) a = a.slice( 0, nullByte ); + + return THREE.LoaderUtils.decodeText( new Uint8Array( a ) ); + + } + + }; + + // FBXTree holds a representation of the FBX data, returned by the TextParser ( FBX ASCII format) + // and BinaryParser( FBX Binary format) + function FBXTree() {} + + FBXTree.prototype = { + + constructor: FBXTree, + + add: function ( key, val ) { + + this[ key ] = val; + + }, + + }; + + // ************** UTILITY FUNCTIONS ************** + + function isFbxFormatBinary( buffer ) { + + var CORRECT = 'Kaydara FBX Binary \0'; + + return buffer.byteLength >= CORRECT.length && CORRECT === convertArrayBufferToString( buffer, 0, CORRECT.length ); + + } + + function isFbxFormatASCII( text ) { + + var CORRECT = [ 'K', 'a', 'y', 'd', 'a', 'r', 'a', '\\', 'F', 'B', 'X', '\\', 'B', 'i', 'n', 'a', 'r', 'y', '\\', '\\' ]; + + var cursor = 0; + + function read( offset ) { + + var result = text[ offset - 1 ]; + text = text.slice( cursor + offset ); + cursor ++; + return result; + + } + + for ( var i = 0; i < CORRECT.length; ++ i ) { + + var num = read( 1 ); + if ( num === CORRECT[ i ] ) { + + return false; + + } + + } + + return true; + + } + + function getFbxVersion( text ) { + + var versionRegExp = /FBXVersion: (\d+)/; + var match = text.match( versionRegExp ); + if ( match ) { + + var version = parseInt( match[ 1 ] ); + return version; + + } + throw new Error( 'THREE.FBXLoader: Cannot find the version number for the file given.' ); + + } + + // Converts FBX ticks into real time seconds. + function convertFBXTimeToSeconds( time ) { + + return time / 46186158000; + + } + + var dataArray = []; + + // extracts the data from the correct position in the FBX array based on indexing type + function getData( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) { + + var index; + + switch ( infoObject.mappingType ) { + + case 'ByPolygonVertex' : + index = polygonVertexIndex; + break; + case 'ByPolygon' : + index = polygonIndex; + break; + case 'ByVertice' : + index = vertexIndex; + break; + case 'AllSame' : + index = infoObject.indices[ 0 ]; + break; + default : + console.warn( 'THREE.FBXLoader: unknown attribute mapping type ' + infoObject.mappingType ); + + } + + if ( infoObject.referenceType === 'IndexToDirect' ) index = infoObject.indices[ index ]; + + var from = index * infoObject.dataSize; + var to = from + infoObject.dataSize; + + return slice( dataArray, infoObject.buffer, from, to ); + + } + + var tempMat = new THREE.Matrix4(); + var tempEuler = new THREE.Euler(); + var tempVec = new THREE.Vector3(); + var translation = new THREE.Vector3(); + var rotation = new THREE.Matrix4(); + + // generate transformation from FBX transform data + // ref: https://help.autodesk.com/view/FBX/2017/ENU/?guid=__files_GUID_10CDD63C_79C1_4F2D_BB28_AD2BE65A02ED_htm + // transformData = { + // eulerOrder: int, + // translation: [], + // rotationOffset: [], + // preRotation + // rotation + // postRotation + // scale + // } + // all entries are optional + function generateTransform( transformData ) { + + var transform = new THREE.Matrix4(); + translation.set( 0, 0, 0 ); + rotation.identity(); + + var order = ( transformData.eulerOrder ) ? getEulerOrder( transformData.eulerOrder ) : getEulerOrder( 0 ); + + if ( transformData.translation ) translation.fromArray( transformData.translation ); + if ( transformData.rotationOffset ) translation.add( tempVec.fromArray( transformData.rotationOffset ) ); + + if ( transformData.rotation ) { + + var array = transformData.rotation.map( THREE.Math.degToRad ); + array.push( order ); + rotation.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + } + + if ( transformData.preRotation ) { + + var array = transformData.preRotation.map( THREE.Math.degToRad ); + array.push( order ); + tempMat.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + rotation.premultiply( tempMat ); + + } + + if ( transformData.postRotation ) { + + var array = transformData.postRotation.map( THREE.Math.degToRad ); + array.push( order ); + tempMat.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + tempMat.getInverse( tempMat ); + + rotation.multiply( tempMat ); + + } + + if ( transformData.scale ) transform.scale( tempVec.fromArray( transformData.scale ) ); + + transform.setPosition( translation ); + transform.multiply( rotation ); + + return transform; + + } + + // Returns the three.js intrinsic Euler order corresponding to FBX extrinsic Euler order + // ref: http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_euler_html + function getEulerOrder( order ) { + + var enums = [ + 'ZYX', // -> XYZ extrinsic + 'YZX', // -> XZY extrinsic + 'XZY', // -> YZX extrinsic + 'ZXY', // -> YXZ extrinsic + 'YXZ', // -> ZXY extrinsic + 'XYZ', // -> ZYX extrinsic + //'SphericXYZ', // not possible to support + ]; + + if ( order === 6 ) { + + console.warn( 'THREE.FBXLoader: unsupported Euler Order: Spherical XYZ. Animations and rotations may be incorrect.' ); + return enums[ 0 ]; + + } + + return enums[ order ]; + + } + + // Parses comma separated list of numbers and returns them an array. + // Used internally by the TextParser + function parseNumberArray( value ) { + + var array = value.split( ',' ).map( function ( val ) { + + return parseFloat( val ); + + } ); + + return array; + + } + + function convertArrayBufferToString( buffer, from, to ) { + + if ( from === undefined ) from = 0; + if ( to === undefined ) to = buffer.byteLength; + + return THREE.LoaderUtils.decodeText( new Uint8Array( buffer, from, to ) ); + + } + + function append( a, b ) { + + for ( var i = 0, j = a.length, l = b.length; i < l; i ++, j ++ ) { + + a[ j ] = b[ i ]; + + } + + } + + function slice( a, b, from, to ) { + + for ( var i = from, j = 0; i < to; i ++, j ++ ) { + + a[ j ] = b[ i ]; + + } + + return a; + + } + + // inject array a2 into array a1 at index + function inject( a1, index, a2 ) { + + return a1.slice( 0, index ).concat( a2 ).concat( a1.slice( index ) ); + + } + + return FBXLoader; + +} )(); diff --git a/daydream/public/libs/FBXLoader_12_09_18.js b/daydream/public/libs/FBXLoader_12_09_18.js new file mode 100644 index 000000000..dfde697a8 --- /dev/null +++ b/daydream/public/libs/FBXLoader_12_09_18.js @@ -0,0 +1,4007 @@ +/** + * @author Kyle-Larson https://github.com/Kyle-Larson + * @author Takahiro https://github.com/takahirox + * @author Lewy Blue https://github.com/looeee + * + * Loader loads FBX file and generates Group representing FBX scene. + * Requires FBX file to be >= 7.0 and in ASCII or >= 6400 in Binary format + * Versions lower than this may load but will probably have errors + * + * Needs Support: + * Morph normals / blend shape normals + * + * FBX format references: + * https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure + * http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_index_html (C++ SDK reference) + * + * Binary format specification: + * https://code.blender.org/2013/08/fbx-binary-file-format-specification/ + */ + + +THREE.FBXLoader = ( function () { + + var fbxTree; + var connections; + var sceneGraph; + + function FBXLoader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + } + + FBXLoader.prototype = { + + constructor: FBXLoader, + + crossOrigin: 'anonymous', + + load: function ( url, onLoad, onProgress, onError ) { + var self = this; + + var resourceDirectory = THREE.LoaderUtils.extractUrlBase( url ); + + var loader = new THREE.FileLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( buffer ) { + + try { + + var scene = self.parse( buffer, resourceDirectory ); + onLoad( scene ); + + } catch ( error ) { + + setTimeout( function () { + + if ( onError ) onError( error ); + + self.manager.itemError( url ); + + }, 0 ); + + } + + }, onProgress, onError ); + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + return this; + + }, + + parse: function ( FBXBuffer, resourceDirectory ) { + + if ( isFbxFormatBinary( FBXBuffer ) ) { + + fbxTree = new BinaryParser().parse( FBXBuffer ); + + } else { + + var FBXText = convertArrayBufferToString( FBXBuffer ); + + if ( ! isFbxFormatASCII( FBXText ) ) { + + throw new Error( 'THREE.FBXLoader: Unknown format.' ); + + } + + if ( getFbxVersion( FBXText ) < 7000 ) { + + throw new Error( 'THREE.FBXLoader: FBX version not supported, FileVersion: ' + getFbxVersion( FBXText ) ); + + } + + fbxTree = new TextParser().parse( FBXText ); + + } + + // console.log( fbxTree ); + + var textureLoader = new THREE.TextureLoader( this.manager ).setPath( resourceDirectory ).setCrossOrigin( this.crossOrigin ); + + return new FBXTreeParser( textureLoader ).parse( fbxTree ); + + } + + }; + + // Parse the FBXTree object returned by the BinaryParser or TextParser and return a THREE.Group + function FBXTreeParser( textureLoader ) { + + this.textureLoader = textureLoader; + + } + + FBXTreeParser.prototype = { + + constructor: FBXTreeParser, + + parse: function () { + + connections = this.parseConnections(); + + var images = this.parseImages(); + var textures = this.parseTextures( images ); + var materials = this.parseMaterials( textures ); + var deformers = this.parseDeformers(); + var geometryMap = new GeometryParser().parse( deformers ); + + this.parseScene( deformers, geometryMap, materials ); + + return sceneGraph; + + }, + + // Parses FBXTree.Connections which holds parent-child connections between objects (e.g. material -> texture, model->geometry ) + // and details the connection type + parseConnections: function () { + + var connectionMap = new Map(); + + if ( 'Connections' in fbxTree ) { + + var rawConnections = fbxTree.Connections.connections; + + rawConnections.forEach( function ( rawConnection ) { + + var fromID = rawConnection[ 0 ]; + var toID = rawConnection[ 1 ]; + var relationship = rawConnection[ 2 ]; + + if ( ! connectionMap.has( fromID ) ) { + + connectionMap.set( fromID, { + parents: [], + children: [] + } ); + + } + + var parentRelationship = { ID: toID, relationship: relationship }; + connectionMap.get( fromID ).parents.push( parentRelationship ); + + if ( ! connectionMap.has( toID ) ) { + + connectionMap.set( toID, { + parents: [], + children: [] + } ); + + } + + var childRelationship = { ID: fromID, relationship: relationship }; + connectionMap.get( toID ).children.push( childRelationship ); + + } ); + + } + + return connectionMap; + + }, + + // Parse FBXTree.Objects.Video for embedded image data + // These images are connected to textures in FBXTree.Objects.Textures + // via FBXTree.Connections. + parseImages: function () { + + var images = {}; + var blobs = {}; + + if ( 'Video' in fbxTree.Objects ) { + + var videoNodes = fbxTree.Objects.Video; + + for ( var nodeID in videoNodes ) { + + var videoNode = videoNodes[ nodeID ]; + + var id = parseInt( nodeID ); + + images[ id ] = videoNode.RelativeFilename || videoNode.Filename; + + // raw image data is in videoNode.Content + if ( 'Content' in videoNode ) { + + var arrayBufferContent = ( videoNode.Content instanceof ArrayBuffer ) && ( videoNode.Content.byteLength > 0 ); + var base64Content = ( typeof videoNode.Content === 'string' ) && ( videoNode.Content !== '' ); + + if ( arrayBufferContent || base64Content ) { + + var image = this.parseImage( videoNodes[ nodeID ] ); + + blobs[ videoNode.RelativeFilename || videoNode.Filename ] = image; + + } + + } + + } + + } + + for ( var id in images ) { + + var filename = images[ id ]; + + if ( blobs[ filename ] !== undefined ) images[ id ] = blobs[ filename ]; + else images[ id ] = images[ id ].split( '\\' ).pop(); + + } + + return images; + + }, + + // Parse embedded image data in FBXTree.Video.Content + parseImage: function ( videoNode ) { + + var content = videoNode.Content; + var fileName = videoNode.RelativeFilename || videoNode.Filename; + var extension = fileName.slice( fileName.lastIndexOf( '.' ) + 1 ).toLowerCase(); + + var type; + + switch ( extension ) { + + case 'bmp': + + type = 'image/bmp'; + break; + + case 'jpg': + case 'jpeg': + + type = 'image/jpeg'; + break; + + case 'png': + + type = 'image/png'; + break; + + case 'tif': + + type = 'image/tiff'; + break; + + case 'tga': + + if ( typeof THREE.TGALoader !== 'function' ) { + + console.warn( 'FBXLoader: THREE.TGALoader is required to load TGA textures' ); + return; + + } else { + + if ( THREE.Loader.Handlers.get( '.tga' ) === null ) { + + THREE.Loader.Handlers.add( /\.tga$/i, new THREE.TGALoader() ); + + } + + type = 'image/tga'; + break; + + } + + default: + + console.warn( 'FBXLoader: Image type "' + extension + '" is not supported.' ); + return; + + } + + if ( typeof content === 'string' ) { // ASCII format + + return 'data:' + type + ';base64,' + content; + + } else { // Binary Format + + var array = new Uint8Array( content ); + return window.URL.createObjectURL( new Blob( [ array ], { type: type } ) ); + + } + + }, + + // Parse nodes in FBXTree.Objects.Texture + // These contain details such as UV scaling, cropping, rotation etc and are connected + // to images in FBXTree.Objects.Video + parseTextures: function ( images ) { + + var textureMap = new Map(); + + if ( 'Texture' in fbxTree.Objects ) { + + var textureNodes = fbxTree.Objects.Texture; + for ( var nodeID in textureNodes ) { + + var texture = this.parseTexture( textureNodes[ nodeID ], images ); + textureMap.set( parseInt( nodeID ), texture ); + + } + + } + + return textureMap; + + }, + + // Parse individual node in FBXTree.Objects.Texture + parseTexture: function ( textureNode, images ) { + + var texture = this.loadTexture( textureNode, images ); + + texture.ID = textureNode.id; + + texture.name = textureNode.attrName; + + var wrapModeU = textureNode.WrapModeU; + var wrapModeV = textureNode.WrapModeV; + + var valueU = wrapModeU !== undefined ? wrapModeU.value : 0; + var valueV = wrapModeV !== undefined ? wrapModeV.value : 0; + + // http://download.autodesk.com/us/fbx/SDKdocs/FBX_SDK_Help/files/fbxsdkref/class_k_fbx_texture.html#889640e63e2e681259ea81061b85143a + // 0: repeat(default), 1: clamp + + texture.wrapS = valueU === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + texture.wrapT = valueV === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + + if ( 'Scaling' in textureNode ) { + + var values = textureNode.Scaling.value; + + texture.repeat.x = values[ 0 ]; + texture.repeat.y = values[ 1 ]; + + } + + return texture; + + }, + + // load a texture specified as a blob or data URI, or via an external URL using THREE.TextureLoader + loadTexture: function ( textureNode, images ) { + + var fileName; + + var currentPath = this.textureLoader.path; + + var children = connections.get( textureNode.id ).children; + + if ( children !== undefined && children.length > 0 && images[ children[ 0 ].ID ] !== undefined ) { + + fileName = images[ children[ 0 ].ID ]; + + if ( fileName.indexOf( 'blob:' ) === 0 || fileName.indexOf( 'data:' ) === 0 ) { + + this.textureLoader.setPath( undefined ); + + } + + } + + var texture; + + var extension = textureNode.FileName.slice( - 3 ).toLowerCase(); + + if ( extension === 'tga' ) { + + var loader = THREE.Loader.Handlers.get( '.tga' ); + + if ( loader === null ) { + + console.warn( 'FBXLoader: TGALoader not found, creating empty placeholder texture for', fileName ); + texture = new THREE.Texture(); + + } else { + + texture = loader.load( fileName ); + + } + + } else if ( extension === 'psd' ) { + + console.warn( 'FBXLoader: PSD textures are not supported, creating empty placeholder texture for', fileName ); + texture = new THREE.Texture(); + + } else { + + texture = this.textureLoader.load( fileName ); + + } + + this.textureLoader.setPath( currentPath ); + + return texture; + + }, + + // Parse nodes in FBXTree.Objects.Material + parseMaterials: function ( textureMap ) { + + var materialMap = new Map(); + + if ( 'Material' in fbxTree.Objects ) { + + var materialNodes = fbxTree.Objects.Material; + + for ( var nodeID in materialNodes ) { + + var material = this.parseMaterial( materialNodes[ nodeID ], textureMap ); + + if ( material !== null ) materialMap.set( parseInt( nodeID ), material ); + + } + + } + + return materialMap; + + }, + + // Parse single node in FBXTree.Objects.Material + // Materials are connected to texture maps in FBXTree.Objects.Textures + // FBX format currently only supports Lambert and Phong shading models + parseMaterial: function ( materialNode, textureMap ) { + + var ID = materialNode.id; + var name = materialNode.attrName; + var type = materialNode.ShadingModel; + + // Case where FBX wraps shading model in property object. + if ( typeof type === 'object' ) { + + type = type.value; + + } + + // Ignore unused materials which don't have any connections. + if ( ! connections.has( ID ) ) return null; + + var parameters = this.parseParameters( materialNode, textureMap, ID ); + + var material; + + switch ( type.toLowerCase() ) { + + case 'phong': + material = new THREE.MeshPhongMaterial(); + break; + case 'lambert': + material = new THREE.MeshLambertMaterial(); + break; + default: + console.warn( 'THREE.FBXLoader: unknown material type "%s". Defaulting to MeshPhongMaterial.', type ); + material = new THREE.MeshPhongMaterial( { color: 0x3300ff } ); + break; + + } + + material.setValues( parameters ); + material.name = name; + + return material; + + }, + + // Parse FBX material and return parameters suitable for a three.js material + // Also parse the texture map and return any textures associated with the material + parseParameters: function ( materialNode, textureMap, ID ) { + + var parameters = {}; + + if ( materialNode.BumpFactor ) { + + parameters.bumpScale = materialNode.BumpFactor.value; + + } + if ( materialNode.Diffuse ) { + + parameters.color = new THREE.Color().fromArray( materialNode.Diffuse.value ); + + } else if ( materialNode.DiffuseColor && materialNode.DiffuseColor.type === 'Color' ) { + + // The blender exporter exports diffuse here instead of in materialNode.Diffuse + parameters.color = new THREE.Color().fromArray( materialNode.DiffuseColor.value ); + + } + if ( materialNode.DisplacementFactor ) { + + parameters.displacementScale = materialNode.DisplacementFactor.value; + + } + if ( materialNode.Emissive ) { + + parameters.emissive = new THREE.Color().fromArray( materialNode.Emissive.value ); + + } else if ( materialNode.EmissiveColor && materialNode.EmissiveColor.type === 'Color' ) { + + // The blender exporter exports emissive color here instead of in materialNode.Emissive + parameters.emissive = new THREE.Color().fromArray( materialNode.EmissiveColor.value ); + + } + if ( materialNode.EmissiveFactor ) { + + parameters.emissiveIntensity = parseFloat( materialNode.EmissiveFactor.value ); + + } + if ( materialNode.Opacity ) { + + parameters.opacity = parseFloat( materialNode.Opacity.value ); + + } + if ( parameters.opacity < 1.0 ) { + + parameters.transparent = true; + + } + if ( materialNode.ReflectionFactor ) { + + parameters.reflectivity = materialNode.ReflectionFactor.value; + + } + if ( materialNode.Shininess ) { + + parameters.shininess = materialNode.Shininess.value; + + } + if ( materialNode.Specular ) { + + parameters.specular = new THREE.Color().fromArray( materialNode.Specular.value ); + + } else if ( materialNode.SpecularColor && materialNode.SpecularColor.type === 'Color' ) { + + // The blender exporter exports specular color here instead of in materialNode.Specular + parameters.specular = new THREE.Color().fromArray( materialNode.SpecularColor.value ); + + } + + var self = this; + connections.get( ID ).children.forEach( function ( child ) { + + var type = child.relationship; + + switch ( type ) { + + case 'Bump': + parameters.bumpMap = self.getTexture( textureMap, child.ID ); + break; + + case 'DiffuseColor': + parameters.map = self.getTexture( textureMap, child.ID ); + break; + + case 'DisplacementColor': + parameters.displacementMap = self.getTexture( textureMap, child.ID ); + break; + + + case 'EmissiveColor': + parameters.emissiveMap = self.getTexture( textureMap, child.ID ); + break; + + case 'NormalMap': + parameters.normalMap = self.getTexture( textureMap, child.ID ); + break; + + case 'ReflectionColor': + parameters.envMap = self.getTexture( textureMap, child.ID ); + parameters.envMap.mapping = THREE.EquirectangularReflectionMapping; + break; + + case 'SpecularColor': + parameters.specularMap = self.getTexture( textureMap, child.ID ); + break; + + case 'TransparentColor': + parameters.alphaMap = self.getTexture( textureMap, child.ID ); + parameters.transparent = true; + break; + + case 'AmbientColor': + case 'ShininessExponent': // AKA glossiness map + case 'SpecularFactor': // AKA specularLevel + case 'VectorDisplacementColor': // NOTE: Seems to be a copy of DisplacementColor + default: + console.warn( 'THREE.FBXLoader: %s map is not supported in three.js, skipping texture.', type ); + break; + + } + + } ); + + return parameters; + + }, + + // get a texture from the textureMap for use by a material. + getTexture: function ( textureMap, id ) { + + // if the texture is a layered texture, just use the first layer and issue a warning + if ( 'LayeredTexture' in fbxTree.Objects && id in fbxTree.Objects.LayeredTexture ) { + + console.warn( 'THREE.FBXLoader: layered textures are not supported in three.js. Discarding all but first layer.' ); + id = connections.get( id ).children[ 0 ].ID; + + } + + return textureMap.get( id ); + + }, + + // Parse nodes in FBXTree.Objects.Deformer + // Deformer node can contain skinning or Vertex Cache animation data, however only skinning is supported here + // Generates map of Skeleton-like objects for use later when generating and binding skeletons. + parseDeformers: function () { + + var skeletons = {}; + var morphTargets = {}; + + if ( 'Deformer' in fbxTree.Objects ) { + + var DeformerNodes = fbxTree.Objects.Deformer; + + for ( var nodeID in DeformerNodes ) { + + var deformerNode = DeformerNodes[ nodeID ]; + + var relationships = connections.get( parseInt( nodeID ) ); + + if ( deformerNode.attrType === 'Skin' ) { + + var skeleton = this.parseSkeleton( relationships, DeformerNodes ); + skeleton.ID = nodeID; + + if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: skeleton attached to more than one geometry is not supported.' ); + skeleton.geometryID = relationships.parents[ 0 ].ID; + + skeletons[ nodeID ] = skeleton; + + } else if ( deformerNode.attrType === 'BlendShape' ) { + + var morphTarget = { + id: nodeID, + }; + + morphTarget.rawTargets = this.parseMorphTargets( relationships, DeformerNodes ); + morphTarget.id = nodeID; + + if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: morph target attached to more than one geometry is not supported.' ); + + morphTargets[ nodeID ] = morphTarget; + + } + + } + + } + + return { + + skeletons: skeletons, + morphTargets: morphTargets, + + }; + + }, + + // Parse single nodes in FBXTree.Objects.Deformer + // The top level skeleton node has type 'Skin' and sub nodes have type 'Cluster' + // Each skin node represents a skeleton and each cluster node represents a bone + parseSkeleton: function ( relationships, deformerNodes ) { + + var rawBones = []; + + relationships.children.forEach( function ( child ) { + + var boneNode = deformerNodes[ child.ID ]; + + if ( boneNode.attrType !== 'Cluster' ) return; + + var rawBone = { + + ID: child.ID, + indices: [], + weights: [], + transform: new THREE.Matrix4().fromArray( boneNode.Transform.a ), + transformLink: new THREE.Matrix4().fromArray( boneNode.TransformLink.a ), + linkMode: boneNode.Mode, + + }; + + if ( 'Indexes' in boneNode ) { + + rawBone.indices = boneNode.Indexes.a; + rawBone.weights = boneNode.Weights.a; + + } + + rawBones.push( rawBone ); + + } ); + + return { + + rawBones: rawBones, + bones: [] + + }; + + }, + + // The top level morph deformer node has type "BlendShape" and sub nodes have type "BlendShapeChannel" + parseMorphTargets: function ( relationships, deformerNodes ) { + + var rawMorphTargets = []; + + for ( var i = 0; i < relationships.children.length; i ++ ) { + + var child = relationships.children[ i ]; + + var morphTargetNode = deformerNodes[ child.ID ]; + + var rawMorphTarget = { + + name: morphTargetNode.attrName, + initialWeight: morphTargetNode.DeformPercent, + id: morphTargetNode.id, + fullWeights: morphTargetNode.FullWeights.a + + }; + + if ( morphTargetNode.attrType !== 'BlendShapeChannel' ) return; + + var targetRelationships = connections.get( parseInt( child.ID ) ); + + targetRelationships.children.forEach( function ( child ) { + + if ( child.relationship === undefined ) rawMorphTarget.geoID = child.ID; + + } ); + + rawMorphTargets.push( rawMorphTarget ); + + } + + return rawMorphTargets; + + }, + + // create the main THREE.Group() to be returned by the loader + parseScene: function ( deformers, geometryMap, materialMap ) { + + sceneGraph = new THREE.Group(); + + var modelMap = this.parseModels( deformers.skeletons, geometryMap, materialMap ); + + var modelNodes = fbxTree.Objects.Model; + + var self = this; + modelMap.forEach( function ( model ) { + + var modelNode = modelNodes[ model.ID ]; + self.setLookAtProperties( model, modelNode ); + + var parentConnections = connections.get( model.ID ).parents; + + parentConnections.forEach( function ( connection ) { + + var parent = modelMap.get( connection.ID ); + if ( parent !== undefined ) parent.add( model ); + + } ); + + if ( model.parent === null ) { + + sceneGraph.add( model ); + + } + + + } ); + + this.bindSkeleton( deformers.skeletons, geometryMap, modelMap ); + + this.createAmbientLight(); + + this.setupMorphMaterials(); + + var animations = new AnimationParser().parse(); + + // if all the models where already combined in a single group, just return that + if ( sceneGraph.children.length === 1 && sceneGraph.children[ 0 ].isGroup ) { + + sceneGraph.children[ 0 ].animations = animations; + sceneGraph = sceneGraph.children[ 0 ]; + + } + + sceneGraph.animations = animations; + + }, + + // parse nodes in FBXTree.Objects.Model + parseModels: function ( skeletons, geometryMap, materialMap ) { + + var modelMap = new Map(); + var modelNodes = fbxTree.Objects.Model; + + for ( var nodeID in modelNodes ) { + + var id = parseInt( nodeID ); + var node = modelNodes[ nodeID ]; + var relationships = connections.get( id ); + + var model = this.buildSkeleton( relationships, skeletons, id, node.attrName ); + + if ( ! model ) { + + switch ( node.attrType ) { + + case 'Camera': + model = this.createCamera( relationships ); + break; + case 'Light': + model = this.createLight( relationships ); + break; + case 'Mesh': + model = this.createMesh( relationships, geometryMap, materialMap ); + break; + case 'NurbsCurve': + model = this.createCurve( relationships, geometryMap ); + break; + case 'LimbNode': // usually associated with a Bone, however if a Bone was not created we'll make a Group instead + case 'Null': + default: + model = new THREE.Group(); + break; + + } + + model.name = THREE.PropertyBinding.sanitizeNodeName( node.attrName ); + model.ID = id; + + } + + this.setModelTransforms( model, node ); + modelMap.set( id, model ); + + } + + return modelMap; + + }, + + buildSkeleton: function ( relationships, skeletons, id, name ) { + + var bone = null; + + relationships.parents.forEach( function ( parent ) { + + for ( var ID in skeletons ) { + + var skeleton = skeletons[ ID ]; + + skeleton.rawBones.forEach( function ( rawBone, i ) { + + if ( rawBone.ID === parent.ID ) { + + var subBone = bone; + bone = new THREE.Bone(); + bone.matrixWorld.copy( rawBone.transformLink ); + + // set name and id here - otherwise in cases where "subBone" is created it will not have a name / id + bone.name = THREE.PropertyBinding.sanitizeNodeName( name ); + bone.ID = id; + + skeleton.bones[ i ] = bone; + + // In cases where a bone is shared between multiple meshes + // duplicate the bone here and and it as a child of the first bone + if ( subBone !== null ) { + + bone.add( subBone ); + + } + + } + + } ); + + } + + } ); + + return bone; + + }, + + // create a THREE.PerspectiveCamera or THREE.OrthographicCamera + createCamera: function ( relationships ) { + + var model; + var cameraAttribute; + + relationships.children.forEach( function ( child ) { + + var attr = fbxTree.Objects.NodeAttribute[ child.ID ]; + + if ( attr !== undefined ) { + + cameraAttribute = attr; + + } + + } ); + + if ( cameraAttribute === undefined ) { + + model = new THREE.Object3D(); + + } else { + + var type = 0; + if ( cameraAttribute.CameraProjectionType !== undefined && cameraAttribute.CameraProjectionType.value === 1 ) { + + type = 1; + + } + + var nearClippingPlane = 1; + if ( cameraAttribute.NearPlane !== undefined ) { + + nearClippingPlane = cameraAttribute.NearPlane.value / 1000; + + } + + var farClippingPlane = 1000; + if ( cameraAttribute.FarPlane !== undefined ) { + + farClippingPlane = cameraAttribute.FarPlane.value / 1000; + + } + + + var width = window.innerWidth; + var height = window.innerHeight; + + if ( cameraAttribute.AspectWidth !== undefined && cameraAttribute.AspectHeight !== undefined ) { + + width = cameraAttribute.AspectWidth.value; + height = cameraAttribute.AspectHeight.value; + + } + + var aspect = width / height; + + var fov = 45; + if ( cameraAttribute.FieldOfView !== undefined ) { + + fov = cameraAttribute.FieldOfView.value; + + } + + var focalLength = cameraAttribute.FocalLength ? cameraAttribute.FocalLength.value : null; + + switch ( type ) { + + case 0: // Perspective + model = new THREE.PerspectiveCamera( fov, aspect, nearClippingPlane, farClippingPlane ); + if ( focalLength !== null ) model.setFocalLength( focalLength ); + break; + + case 1: // Orthographic + model = new THREE.OrthographicCamera( - width / 2, width / 2, height / 2, - height / 2, nearClippingPlane, farClippingPlane ); + break; + + default: + console.warn( 'THREE.FBXLoader: Unknown camera type ' + type + '.' ); + model = new THREE.Object3D(); + break; + + } + + } + + return model; + + }, + + // Create a THREE.DirectionalLight, THREE.PointLight or THREE.SpotLight + createLight: function ( relationships ) { + + var model; + var lightAttribute; + + relationships.children.forEach( function ( child ) { + + var attr = fbxTree.Objects.NodeAttribute[ child.ID ]; + + if ( attr !== undefined ) { + + lightAttribute = attr; + + } + + } ); + + if ( lightAttribute === undefined ) { + + model = new THREE.Object3D(); + + } else { + + var type; + + // LightType can be undefined for Point lights + if ( lightAttribute.LightType === undefined ) { + + type = 0; + + } else { + + type = lightAttribute.LightType.value; + + } + + var color = 0xffffff; + + if ( lightAttribute.Color !== undefined ) { + + color = new THREE.Color().fromArray( lightAttribute.Color.value ); + + } + + var intensity = ( lightAttribute.Intensity === undefined ) ? 1 : lightAttribute.Intensity.value / 100; + + // light disabled + if ( lightAttribute.CastLightOnObject !== undefined && lightAttribute.CastLightOnObject.value === 0 ) { + + intensity = 0; + + } + + var distance = 0; + if ( lightAttribute.FarAttenuationEnd !== undefined ) { + + if ( lightAttribute.EnableFarAttenuation !== undefined && lightAttribute.EnableFarAttenuation.value === 0 ) { + + distance = 0; + + } else { + + distance = lightAttribute.FarAttenuationEnd.value; + + } + + } + + // TODO: could this be calculated linearly from FarAttenuationStart to FarAttenuationEnd? + var decay = 1; + + switch ( type ) { + + case 0: // Point + model = new THREE.PointLight( color, intensity, distance, decay ); + break; + + case 1: // Directional + model = new THREE.DirectionalLight( color, intensity ); + break; + + case 2: // Spot + var angle = Math.PI / 3; + + if ( lightAttribute.InnerAngle !== undefined ) { + + angle = THREE.Math.degToRad( lightAttribute.InnerAngle.value ); + + } + + var penumbra = 0; + if ( lightAttribute.OuterAngle !== undefined ) { + + // TODO: this is not correct - FBX calculates outer and inner angle in degrees + // with OuterAngle > InnerAngle && OuterAngle <= Math.PI + // while three.js uses a penumbra between (0, 1) to attenuate the inner angle + penumbra = THREE.Math.degToRad( lightAttribute.OuterAngle.value ); + penumbra = Math.max( penumbra, 1 ); + + } + + model = new THREE.SpotLight( color, intensity, distance, angle, penumbra, decay ); + break; + + default: + console.warn( 'THREE.FBXLoader: Unknown light type ' + lightAttribute.LightType.value + ', defaulting to a THREE.PointLight.' ); + model = new THREE.PointLight( color, intensity ); + break; + + } + + if ( lightAttribute.CastShadows !== undefined && lightAttribute.CastShadows.value === 1 ) { + + model.castShadow = true; + + } + + } + + return model; + + }, + + createMesh: function ( relationships, geometryMap, materialMap ) { + + var model; + var geometry = null; + var material = null; + var materials = []; + + // get geometry and materials(s) from connections + relationships.children.forEach( function ( child ) { + + if ( geometryMap.has( child.ID ) ) { + + geometry = geometryMap.get( child.ID ); + + } + + if ( materialMap.has( child.ID ) ) { + + materials.push( materialMap.get( child.ID ) ); + + } + + } ); + + if ( materials.length > 1 ) { + + material = materials; + + } else if ( materials.length > 0 ) { + + material = materials[ 0 ]; + + } else { + + material = new THREE.MeshPhongMaterial( { color: 0xcccccc } ); + materials.push( material ); + + } + + if ( 'color' in geometry.attributes ) { + + materials.forEach( function ( material ) { + + material.vertexColors = THREE.VertexColors; + + } ); + + } + + if ( geometry.FBX_Deformer ) { + + materials.forEach( function ( material ) { + + material.skinning = true; + + } ); + + model = new THREE.SkinnedMesh( geometry, material ); + + } else { + + model = new THREE.Mesh( geometry, material ); + + } + + return model; + + }, + + createCurve: function ( relationships, geometryMap ) { + + var geometry = relationships.children.reduce( function ( geo, child ) { + + if ( geometryMap.has( child.ID ) ) geo = geometryMap.get( child.ID ); + + return geo; + + }, null ); + + // FBX does not list materials for Nurbs lines, so we'll just put our own in here. + var material = new THREE.LineBasicMaterial( { color: 0x3300ff, linewidth: 1 } ); + return new THREE.Line( geometry, material ); + + }, + + // parse the model node for transform details and apply them to the model + setModelTransforms: function ( model, modelNode ) { + + var transformData = {}; + + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = parseInt( modelNode.RotationOrder.value ); + if ( 'Lcl_Translation' in modelNode ) transformData.translation = modelNode.Lcl_Translation.value; + if ( 'RotationOffset' in modelNode ) transformData.rotationOffset = modelNode.RotationOffset.value; + if ( 'Lcl_Rotation' in modelNode ) transformData.rotation = modelNode.Lcl_Rotation.value; + if ( 'PreRotation' in modelNode ) transformData.preRotation = modelNode.PreRotation.value; + if ( 'PostRotation' in modelNode ) transformData.postRotation = modelNode.PostRotation.value; + if ( 'Lcl_Scaling' in modelNode ) transformData.scale = modelNode.Lcl_Scaling.value; + + var transform = generateTransform( transformData ); + + model.applyMatrix( transform ); + + }, + + setLookAtProperties: function ( model, modelNode ) { + + if ( 'LookAtProperty' in modelNode ) { + + var children = connections.get( model.ID ).children; + + children.forEach( function ( child ) { + + if ( child.relationship === 'LookAtProperty' ) { + + var lookAtTarget = fbxTree.Objects.Model[ child.ID ]; + + if ( 'Lcl_Translation' in lookAtTarget ) { + + var pos = lookAtTarget.Lcl_Translation.value; + + // DirectionalLight, SpotLight + if ( model.target !== undefined ) { + + model.target.position.fromArray( pos ); + sceneGraph.add( model.target ); + + } else { // Cameras and other Object3Ds + + model.lookAt( new THREE.Vector3().fromArray( pos ) ); + + } + + } + + } + + } ); + + } + + }, + + bindSkeleton: function ( skeletons, geometryMap, modelMap ) { + + var bindMatrices = this.parsePoseNodes(); + + for ( var ID in skeletons ) { + + var skeleton = skeletons[ ID ]; + + var parents = connections.get( parseInt( skeleton.ID ) ).parents; + + parents.forEach( function ( parent ) { + + if ( geometryMap.has( parent.ID ) ) { + + var geoID = parent.ID; + var geoRelationships = connections.get( geoID ); + + geoRelationships.parents.forEach( function ( geoConnParent ) { + + if ( modelMap.has( geoConnParent.ID ) ) { + + var model = modelMap.get( geoConnParent.ID ); + + model.bind( new THREE.Skeleton( skeleton.bones ), bindMatrices[ geoConnParent.ID ] ); + + } + + } ); + + } + + } ); + + } + + }, + + parsePoseNodes: function () { + + var bindMatrices = {}; + + if ( 'Pose' in fbxTree.Objects ) { + + var BindPoseNode = fbxTree.Objects.Pose; + + for ( var nodeID in BindPoseNode ) { + + if ( BindPoseNode[ nodeID ].attrType === 'BindPose' ) { + + var poseNodes = BindPoseNode[ nodeID ].PoseNode; + + if ( Array.isArray( poseNodes ) ) { + + poseNodes.forEach( function ( poseNode ) { + + bindMatrices[ poseNode.Node ] = new THREE.Matrix4().fromArray( poseNode.Matrix.a ); + + } ); + + } else { + + bindMatrices[ poseNodes.Node ] = new THREE.Matrix4().fromArray( poseNodes.Matrix.a ); + + } + + } + + } + + } + + return bindMatrices; + + }, + + // Parse ambient color in FBXTree.GlobalSettings - if it's not set to black (default), create an ambient light + createAmbientLight: function () { + + if ( 'GlobalSettings' in fbxTree && 'AmbientColor' in fbxTree.GlobalSettings ) { + + var ambientColor = fbxTree.GlobalSettings.AmbientColor.value; + var r = ambientColor[ 0 ]; + var g = ambientColor[ 1 ]; + var b = ambientColor[ 2 ]; + + if ( r !== 0 || g !== 0 || b !== 0 ) { + + var color = new THREE.Color( r, g, b ); + sceneGraph.add( new THREE.AmbientLight( color, 1 ) ); + + } + + } + + }, + + setupMorphMaterials: function () { + + sceneGraph.traverse( function ( child ) { + + if ( child.isMesh ) { + + if ( child.geometry.morphAttributes.position || child.geometry.morphAttributes.normal ) { + + var uuid = child.uuid; + var matUuid = child.material.uuid; + + // if a geometry has morph targets, it cannot share the material with other geometries + var sharedMat = false; + + sceneGraph.traverse( function ( child ) { + + if ( child.isMesh ) { + + if ( child.material.uuid === matUuid && child.uuid !== uuid ) sharedMat = true; + + } + + } ); + + if ( sharedMat === true ) child.material = child.material.clone(); + + child.material.morphTargets = true; + + } + + } + + } ); + + }, + + }; + + // parse Geometry data from FBXTree and return map of BufferGeometries + function GeometryParser() {} + + GeometryParser.prototype = { + + constructor: GeometryParser, + + // Parse nodes in FBXTree.Objects.Geometry + parse: function ( deformers ) { + + var geometryMap = new Map(); + + if ( 'Geometry' in fbxTree.Objects ) { + + var geoNodes = fbxTree.Objects.Geometry; + + for ( var nodeID in geoNodes ) { + + var relationships = connections.get( parseInt( nodeID ) ); + var geo = this.parseGeometry( relationships, geoNodes[ nodeID ], deformers ); + + geometryMap.set( parseInt( nodeID ), geo ); + + } + + } + + return geometryMap; + + }, + + // Parse single node in FBXTree.Objects.Geometry + parseGeometry: function ( relationships, geoNode, deformers ) { + + switch ( geoNode.attrType ) { + + case 'Mesh': + return this.parseMeshGeometry( relationships, geoNode, deformers ); + break; + + case 'NurbsCurve': + return this.parseNurbsGeometry( geoNode ); + break; + + } + + }, + + // Parse single node mesh geometry in FBXTree.Objects.Geometry + parseMeshGeometry: function ( relationships, geoNode, deformers ) { + + var skeletons = deformers.skeletons; + var morphTargets = deformers.morphTargets; + + var modelNodes = relationships.parents.map( function ( parent ) { + + return fbxTree.Objects.Model[ parent.ID ]; + + } ); + + // don't create geometry if it is not associated with any models + if ( modelNodes.length === 0 ) return; + + var skeleton = relationships.children.reduce( function ( skeleton, child ) { + + if ( skeletons[ child.ID ] !== undefined ) skeleton = skeletons[ child.ID ]; + + return skeleton; + + }, null ); + + var morphTarget = relationships.children.reduce( function ( morphTarget, child ) { + + if ( morphTargets[ child.ID ] !== undefined ) morphTarget = morphTargets[ child.ID ]; + + return morphTarget; + + }, null ); + + // TODO: if there is more than one model associated with the geometry, AND the models have + // different geometric transforms, then this will cause problems + // if ( modelNodes.length > 1 ) { } + + // For now just assume one model and get the preRotations from that + var modelNode = modelNodes[ 0 ]; + + var transformData = {}; + + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = modelNode.RotationOrder.value; + if ( 'GeometricTranslation' in modelNode ) transformData.translation = modelNode.GeometricTranslation.value; + if ( 'GeometricRotation' in modelNode ) transformData.rotation = modelNode.GeometricRotation.value; + if ( 'GeometricScaling' in modelNode ) transformData.scale = modelNode.GeometricScaling.value; + + var transform = generateTransform( transformData ); + + return this.genGeometry( geoNode, skeleton, morphTarget, transform ); + + }, + + // Generate a THREE.BufferGeometry from a node in FBXTree.Objects.Geometry + genGeometry: function ( geoNode, skeleton, morphTarget, preTransform ) { + + var geo = new THREE.BufferGeometry(); + if ( geoNode.attrName ) geo.name = geoNode.attrName; + + var geoInfo = this.parseGeoNode( geoNode, skeleton ); + var buffers = this.genBuffers( geoInfo ); + + var positionAttribute = new THREE.Float32BufferAttribute( buffers.vertex, 3 ); + + preTransform.applyToBufferAttribute( positionAttribute ); + + geo.addAttribute( 'position', positionAttribute ); + + if ( buffers.colors.length > 0 ) { + + geo.addAttribute( 'color', new THREE.Float32BufferAttribute( buffers.colors, 3 ) ); + + } + + if ( skeleton ) { + + geo.addAttribute( 'skinIndex', new THREE.Uint16BufferAttribute( buffers.weightsIndices, 4 ) ); + + geo.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( buffers.vertexWeights, 4 ) ); + + // used later to bind the skeleton to the model + geo.FBX_Deformer = skeleton; + + } + + if ( buffers.normal.length > 0 ) { + + var normalAttribute = new THREE.Float32BufferAttribute( buffers.normal, 3 ); + + var normalMatrix = new THREE.Matrix3().getNormalMatrix( preTransform ); + normalMatrix.applyToBufferAttribute( normalAttribute ); + + geo.addAttribute( 'normal', normalAttribute ); + + } + + buffers.uvs.forEach( function ( uvBuffer, i ) { + + // subsequent uv buffers are called 'uv1', 'uv2', ... + var name = 'uv' + ( i + 1 ).toString(); + + // the first uv buffer is just called 'uv' + if ( i === 0 ) { + + name = 'uv'; + + } + + geo.addAttribute( name, new THREE.Float32BufferAttribute( buffers.uvs[ i ], 2 ) ); + + } ); + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + // Convert the material indices of each vertex into rendering groups on the geometry. + var prevMaterialIndex = buffers.materialIndex[ 0 ]; + var startIndex = 0; + + buffers.materialIndex.forEach( function ( currentIndex, i ) { + + if ( currentIndex !== prevMaterialIndex ) { + + geo.addGroup( startIndex, i - startIndex, prevMaterialIndex ); + + prevMaterialIndex = currentIndex; + startIndex = i; + + } + + } ); + + // the loop above doesn't add the last group, do that here. + if ( geo.groups.length > 0 ) { + + var lastGroup = geo.groups[ geo.groups.length - 1 ]; + var lastIndex = lastGroup.start + lastGroup.count; + + if ( lastIndex !== buffers.materialIndex.length ) { + + geo.addGroup( lastIndex, buffers.materialIndex.length - lastIndex, prevMaterialIndex ); + + } + + } + + // case where there are multiple materials but the whole geometry is only + // using one of them + if ( geo.groups.length === 0 ) { + + geo.addGroup( 0, buffers.materialIndex.length, buffers.materialIndex[ 0 ] ); + + } + + } + + this.addMorphTargets( geo, geoNode, morphTarget, preTransform ); + + return geo; + + }, + + parseGeoNode: function ( geoNode, skeleton ) { + + var geoInfo = {}; + + geoInfo.vertexPositions = ( geoNode.Vertices !== undefined ) ? geoNode.Vertices.a : []; + geoInfo.vertexIndices = ( geoNode.PolygonVertexIndex !== undefined ) ? geoNode.PolygonVertexIndex.a : []; + + if ( geoNode.LayerElementColor ) { + + geoInfo.color = this.parseVertexColors( geoNode.LayerElementColor[ 0 ] ); + + } + + if ( geoNode.LayerElementMaterial ) { + + geoInfo.material = this.parseMaterialIndices( geoNode.LayerElementMaterial[ 0 ] ); + + } + + if ( geoNode.LayerElementNormal ) { + + geoInfo.normal = this.parseNormals( geoNode.LayerElementNormal[ 0 ] ); + + } + + if ( geoNode.LayerElementUV ) { + + geoInfo.uv = []; + + var i = 0; + while ( geoNode.LayerElementUV[ i ] ) { + + geoInfo.uv.push( this.parseUVs( geoNode.LayerElementUV[ i ] ) ); + i ++; + + } + + } + + geoInfo.weightTable = {}; + + if ( skeleton !== null ) { + + geoInfo.skeleton = skeleton; + + skeleton.rawBones.forEach( function ( rawBone, i ) { + + // loop over the bone's vertex indices and weights + rawBone.indices.forEach( function ( index, j ) { + + if ( geoInfo.weightTable[ index ] === undefined ) geoInfo.weightTable[ index ] = []; + + geoInfo.weightTable[ index ].push( { + + id: i, + weight: rawBone.weights[ j ], + + } ); + + } ); + + } ); + + } + + return geoInfo; + + }, + + genBuffers: function ( geoInfo ) { + + var buffers = { + vertex: [], + normal: [], + colors: [], + uvs: [], + materialIndex: [], + vertexWeights: [], + weightsIndices: [], + }; + + var polygonIndex = 0; + var faceLength = 0; + var displayedWeightsWarning = false; + + // these will hold data for a single face + var facePositionIndexes = []; + var faceNormals = []; + var faceColors = []; + var faceUVs = []; + var faceWeights = []; + var faceWeightIndices = []; + + var self = this; + geoInfo.vertexIndices.forEach( function ( vertexIndex, polygonVertexIndex ) { + + var endOfFace = false; + + // Face index and vertex index arrays are combined in a single array + // A cube with quad faces looks like this: + // PolygonVertexIndex: *24 { + // a: 0, 1, 3, -3, 2, 3, 5, -5, 4, 5, 7, -7, 6, 7, 1, -1, 1, 7, 5, -4, 6, 0, 2, -5 + // } + // Negative numbers mark the end of a face - first face here is 0, 1, 3, -3 + // to find index of last vertex bit shift the index: ^ - 1 + if ( vertexIndex < 0 ) { + + vertexIndex = vertexIndex ^ - 1; // equivalent to ( x * -1 ) - 1 + endOfFace = true; + + } + + var weightIndices = []; + var weights = []; + + facePositionIndexes.push( vertexIndex * 3, vertexIndex * 3 + 1, vertexIndex * 3 + 2 ); + + if ( geoInfo.color ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.color ); + + faceColors.push( data[ 0 ], data[ 1 ], data[ 2 ] ); + + } + + if ( geoInfo.skeleton ) { + + if ( geoInfo.weightTable[ vertexIndex ] !== undefined ) { + + geoInfo.weightTable[ vertexIndex ].forEach( function ( wt ) { + + weights.push( wt.weight ); + weightIndices.push( wt.id ); + + } ); + + + } + + if ( weights.length > 4 ) { + + if ( ! displayedWeightsWarning ) { + + console.warn( 'THREE.FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights.' ); + displayedWeightsWarning = true; + + } + + var wIndex = [ 0, 0, 0, 0 ]; + var Weight = [ 0, 0, 0, 0 ]; + + weights.forEach( function ( weight, weightIndex ) { + + var currentWeight = weight; + var currentIndex = weightIndices[ weightIndex ]; + + Weight.forEach( function ( comparedWeight, comparedWeightIndex, comparedWeightArray ) { + + if ( currentWeight > comparedWeight ) { + + comparedWeightArray[ comparedWeightIndex ] = currentWeight; + currentWeight = comparedWeight; + + var tmp = wIndex[ comparedWeightIndex ]; + wIndex[ comparedWeightIndex ] = currentIndex; + currentIndex = tmp; + + } + + } ); + + } ); + + weightIndices = wIndex; + weights = Weight; + + } + + // if the weight array is shorter than 4 pad with 0s + while ( weights.length < 4 ) { + + weights.push( 0 ); + weightIndices.push( 0 ); + + } + + for ( var i = 0; i < 4; ++ i ) { + + faceWeights.push( weights[ i ] ); + faceWeightIndices.push( weightIndices[ i ] ); + + } + + } + + if ( geoInfo.normal ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.normal ); + + faceNormals.push( data[ 0 ], data[ 1 ], data[ 2 ] ); + + } + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + var materialIndex = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.material )[ 0 ]; + + } + + if ( geoInfo.uv ) { + + geoInfo.uv.forEach( function ( uv, i ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, uv ); + + if ( faceUVs[ i ] === undefined ) { + + faceUVs[ i ] = []; + + } + + faceUVs[ i ].push( data[ 0 ] ); + faceUVs[ i ].push( data[ 1 ] ); + + } ); + + } + + faceLength ++; + + if ( endOfFace ) { + + self.genFace( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ); + + polygonIndex ++; + faceLength = 0; + + // reset arrays for the next face + facePositionIndexes = []; + faceNormals = []; + faceColors = []; + faceUVs = []; + faceWeights = []; + faceWeightIndices = []; + + } + + } ); + + return buffers; + + }, + + // Generate data for a single face in a geometry. If the face is a quad then split it into 2 tris + genFace: function ( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ) { + + for ( var i = 2; i < faceLength; i ++ ) { + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 0 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 2 ] ] ); + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 2 ] ] ); + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 2 ] ] ); + + if ( geoInfo.skeleton ) { + + buffers.vertexWeights.push( faceWeights[ 0 ] ); + buffers.vertexWeights.push( faceWeights[ 1 ] ); + buffers.vertexWeights.push( faceWeights[ 2 ] ); + buffers.vertexWeights.push( faceWeights[ 3 ] ); + + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 1 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 2 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 3 ] ); + + buffers.vertexWeights.push( faceWeights[ i * 4 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 1 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 2 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ 0 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ i * 4 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 3 ] ); + + } + + if ( geoInfo.color ) { + + buffers.colors.push( faceColors[ 0 ] ); + buffers.colors.push( faceColors[ 1 ] ); + buffers.colors.push( faceColors[ 2 ] ); + + buffers.colors.push( faceColors[ ( i - 1 ) * 3 ] ); + buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 1 ] ); + buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 2 ] ); + + buffers.colors.push( faceColors[ i * 3 ] ); + buffers.colors.push( faceColors[ i * 3 + 1 ] ); + buffers.colors.push( faceColors[ i * 3 + 2 ] ); + + } + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + buffers.materialIndex.push( materialIndex ); + buffers.materialIndex.push( materialIndex ); + buffers.materialIndex.push( materialIndex ); + + } + + if ( geoInfo.normal ) { + + buffers.normal.push( faceNormals[ 0 ] ); + buffers.normal.push( faceNormals[ 1 ] ); + buffers.normal.push( faceNormals[ 2 ] ); + + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 ] ); + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 1 ] ); + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 2 ] ); + + buffers.normal.push( faceNormals[ i * 3 ] ); + buffers.normal.push( faceNormals[ i * 3 + 1 ] ); + buffers.normal.push( faceNormals[ i * 3 + 2 ] ); + + } + + if ( geoInfo.uv ) { + + geoInfo.uv.forEach( function ( uv, j ) { + + if ( buffers.uvs[ j ] === undefined ) buffers.uvs[ j ] = []; + + buffers.uvs[ j ].push( faceUVs[ j ][ 0 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ 1 ] ); + + buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 + 1 ] ); + + buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 + 1 ] ); + + } ); + + } + + } + + }, + + addMorphTargets: function ( parentGeo, parentGeoNode, morphTarget, preTransform ) { + + if ( morphTarget === null ) return; + + parentGeo.morphAttributes.position = []; + // parentGeo.morphAttributes.normal = []; // not implemented + + var self = this; + morphTarget.rawTargets.forEach( function ( rawTarget ) { + + var morphGeoNode = fbxTree.Objects.Geometry[ rawTarget.geoID ]; + + if ( morphGeoNode !== undefined ) { + + self.genMorphGeometry( parentGeo, parentGeoNode, morphGeoNode, preTransform, rawTarget.name ); + + } + + } ); + + }, + + // a morph geometry node is similar to a standard node, and the node is also contained + // in FBXTree.Objects.Geometry, however it can only have attributes for position, normal + // and a special attribute Index defining which vertices of the original geometry are affected + // Normal and position attributes only have data for the vertices that are affected by the morph + genMorphGeometry: function ( parentGeo, parentGeoNode, morphGeoNode, preTransform, name ) { + + var morphGeo = new THREE.BufferGeometry(); + if ( morphGeoNode.attrName ) morphGeo.name = morphGeoNode.attrName; + + var vertexIndices = ( parentGeoNode.PolygonVertexIndex !== undefined ) ? parentGeoNode.PolygonVertexIndex.a : []; + + // make a copy of the parent's vertex positions + var vertexPositions = ( parentGeoNode.Vertices !== undefined ) ? parentGeoNode.Vertices.a.slice() : []; + + var morphPositions = ( morphGeoNode.Vertices !== undefined ) ? morphGeoNode.Vertices.a : []; + var indices = ( morphGeoNode.Indexes !== undefined ) ? morphGeoNode.Indexes.a : []; + + for ( var i = 0; i < indices.length; i ++ ) { + + var morphIndex = indices[ i ] * 3; + + // FBX format uses blend shapes rather than morph targets. This can be converted + // by additively combining the blend shape positions with the original geometry's positions + vertexPositions[ morphIndex ] += morphPositions[ i * 3 ]; + vertexPositions[ morphIndex + 1 ] += morphPositions[ i * 3 + 1 ]; + vertexPositions[ morphIndex + 2 ] += morphPositions[ i * 3 + 2 ]; + + } + + // TODO: add morph normal support + var morphGeoInfo = { + vertexIndices: vertexIndices, + vertexPositions: vertexPositions, + }; + + var morphBuffers = this.genBuffers( morphGeoInfo ); + + var positionAttribute = new THREE.Float32BufferAttribute( morphBuffers.vertex, 3 ); + positionAttribute.name = name || morphGeoNode.attrName; + + preTransform.applyToBufferAttribute( positionAttribute ); + + parentGeo.morphAttributes.position.push( positionAttribute ); + + }, + + // Parse normal from FBXTree.Objects.Geometry.LayerElementNormal if it exists + parseNormals: function ( NormalNode ) { + + var mappingType = NormalNode.MappingInformationType; + var referenceType = NormalNode.ReferenceInformationType; + var buffer = NormalNode.Normals.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + if ( 'NormalIndex' in NormalNode ) { + + indexBuffer = NormalNode.NormalIndex.a; + + } else if ( 'NormalsIndex' in NormalNode ) { + + indexBuffer = NormalNode.NormalsIndex.a; + + } + + } + + return { + dataSize: 3, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse UVs from FBXTree.Objects.Geometry.LayerElementUV if it exists + parseUVs: function ( UVNode ) { + + var mappingType = UVNode.MappingInformationType; + var referenceType = UVNode.ReferenceInformationType; + var buffer = UVNode.UV.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + indexBuffer = UVNode.UVIndex.a; + + } + + return { + dataSize: 2, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse Vertex Colors from FBXTree.Objects.Geometry.LayerElementColor if it exists + parseVertexColors: function ( ColorNode ) { + + var mappingType = ColorNode.MappingInformationType; + var referenceType = ColorNode.ReferenceInformationType; + var buffer = ColorNode.Colors.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + indexBuffer = ColorNode.ColorIndex.a; + + } + + return { + dataSize: 4, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse mapping and material data in FBXTree.Objects.Geometry.LayerElementMaterial if it exists + parseMaterialIndices: function ( MaterialNode ) { + + var mappingType = MaterialNode.MappingInformationType; + var referenceType = MaterialNode.ReferenceInformationType; + + if ( mappingType === 'NoMappingInformation' ) { + + return { + dataSize: 1, + buffer: [ 0 ], + indices: [ 0 ], + mappingType: 'AllSame', + referenceType: referenceType + }; + + } + + var materialIndexBuffer = MaterialNode.Materials.a; + + // Since materials are stored as indices, there's a bit of a mismatch between FBX and what + // we expect.So we create an intermediate buffer that points to the index in the buffer, + // for conforming with the other functions we've written for other data. + var materialIndices = []; + + for ( var i = 0; i < materialIndexBuffer.length; ++ i ) { + + materialIndices.push( i ); + + } + + return { + dataSize: 1, + buffer: materialIndexBuffer, + indices: materialIndices, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Generate a NurbGeometry from a node in FBXTree.Objects.Geometry + parseNurbsGeometry: function ( geoNode ) { + + if ( THREE.NURBSCurve === undefined ) { + + console.error( 'THREE.FBXLoader: The loader relies on THREE.NURBSCurve for any nurbs present in the model. Nurbs will show up as empty geometry.' ); + return new THREE.BufferGeometry(); + + } + + var order = parseInt( geoNode.Order ); + + if ( isNaN( order ) ) { + + console.error( 'THREE.FBXLoader: Invalid Order %s given for geometry ID: %s', geoNode.Order, geoNode.id ); + return new THREE.BufferGeometry(); + + } + + var degree = order - 1; + + var knots = geoNode.KnotVector.a; + var controlPoints = []; + var pointsValues = geoNode.Points.a; + + for ( var i = 0, l = pointsValues.length; i < l; i += 4 ) { + + controlPoints.push( new THREE.Vector4().fromArray( pointsValues, i ) ); + + } + + var startKnot, endKnot; + + if ( geoNode.Form === 'Closed' ) { + + controlPoints.push( controlPoints[ 0 ] ); + + } else if ( geoNode.Form === 'Periodic' ) { + + startKnot = degree; + endKnot = knots.length - 1 - startKnot; + + for ( var i = 0; i < degree; ++ i ) { + + controlPoints.push( controlPoints[ i ] ); + + } + + } + + var curve = new THREE.NURBSCurve( degree, knots, controlPoints, startKnot, endKnot ); + var vertices = curve.getPoints( controlPoints.length * 7 ); + + var positions = new Float32Array( vertices.length * 3 ); + + vertices.forEach( function ( vertex, i ) { + + vertex.toArray( positions, i * 3 ); + + } ); + + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + + return geometry; + + }, + + }; + + // parse animation data from FBXTree + function AnimationParser() {} + + AnimationParser.prototype = { + + constructor: AnimationParser, + + // take raw animation clips and turn them into three.js animation clips + parse: function () { + + var animationClips = []; + + + var rawClips = this.parseClips(); + + if ( rawClips === undefined ) return; + + for ( var key in rawClips ) { + + var rawClip = rawClips[ key ]; + + var clip = this.addClip( rawClip ); + + animationClips.push( clip ); + + } + + return animationClips; + + }, + + parseClips: function () { + + // since the actual transformation data is stored in FBXTree.Objects.AnimationCurve, + // if this is undefined we can safely assume there are no animations + if ( fbxTree.Objects.AnimationCurve === undefined ) return undefined; + + var curveNodesMap = this.parseAnimationCurveNodes(); + + this.parseAnimationCurves( curveNodesMap ); + + var layersMap = this.parseAnimationLayers( curveNodesMap ); + var rawClips = this.parseAnimStacks( layersMap ); + + return rawClips; + + }, + + // parse nodes in FBXTree.Objects.AnimationCurveNode + // each AnimationCurveNode holds data for an animation transform for a model (e.g. left arm rotation ) + // and is referenced by an AnimationLayer + parseAnimationCurveNodes: function () { + + var rawCurveNodes = fbxTree.Objects.AnimationCurveNode; + + var curveNodesMap = new Map(); + + for ( var nodeID in rawCurveNodes ) { + + var rawCurveNode = rawCurveNodes[ nodeID ]; + + if ( rawCurveNode.attrName.match( /S|R|T|DeformPercent/ ) !== null ) { + + var curveNode = { + + id: rawCurveNode.id, + attr: rawCurveNode.attrName, + curves: {}, + + }; + + curveNodesMap.set( curveNode.id, curveNode ); + + } + + } + + return curveNodesMap; + + }, + + // parse nodes in FBXTree.Objects.AnimationCurve and connect them up to + // previously parsed AnimationCurveNodes. Each AnimationCurve holds data for a single animated + // axis ( e.g. times and values of x rotation) + parseAnimationCurves: function ( curveNodesMap ) { + + var rawCurves = fbxTree.Objects.AnimationCurve; + + // TODO: Many values are identical up to roundoff error, but won't be optimised + // e.g. position times: [0, 0.4, 0. 8] + // position values: [7.23538335023477e-7, 93.67518615722656, -0.9982695579528809, 7.23538335023477e-7, 93.67518615722656, -0.9982695579528809, 7.235384487103147e-7, 93.67520904541016, -0.9982695579528809] + // clearly, this should be optimised to + // times: [0], positions [7.23538335023477e-7, 93.67518615722656, -0.9982695579528809] + // this shows up in nearly every FBX file, and generally time array is length > 100 + + for ( var nodeID in rawCurves ) { + + var animationCurve = { + + id: rawCurves[ nodeID ].id, + times: rawCurves[ nodeID ].KeyTime.a.map( convertFBXTimeToSeconds ), + values: rawCurves[ nodeID ].KeyValueFloat.a, + + }; + + var relationships = connections.get( animationCurve.id ); + + if ( relationships !== undefined ) { + + var animationCurveID = relationships.parents[ 0 ].ID; + var animationCurveRelationship = relationships.parents[ 0 ].relationship; + + if ( animationCurveRelationship.match( /X/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'x' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /Y/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'y' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /Z/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'z' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /d|DeformPercent/ ) && curveNodesMap.has( animationCurveID ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'morph' ] = animationCurve; + + } + + } + + } + + }, + + // parse nodes in FBXTree.Objects.AnimationLayer. Each layers holds references + // to various AnimationCurveNodes and is referenced by an AnimationStack node + // note: theoretically a stack can have multiple layers, however in practice there always seems to be one per stack + parseAnimationLayers: function ( curveNodesMap ) { + + var rawLayers = fbxTree.Objects.AnimationLayer; + + var layersMap = new Map(); + + for ( var nodeID in rawLayers ) { + + var layerCurveNodes = []; + + var connection = connections.get( parseInt( nodeID ) ); + + if ( connection !== undefined ) { + + // all the animationCurveNodes used in the layer + var children = connection.children; + + var self = this; + children.forEach( function ( child, i ) { + + if ( curveNodesMap.has( child.ID ) ) { + + var curveNode = curveNodesMap.get( child.ID ); + + // check that the curves are defined for at least one axis, otherwise ignore the curveNode + if ( curveNode.curves.x !== undefined || curveNode.curves.y !== undefined || curveNode.curves.z !== undefined ) { + + if ( layerCurveNodes[ i ] === undefined ) { + + var modelID; + + connections.get( child.ID ).parents.forEach( function ( parent ) { + + if ( parent.relationship !== undefined ) modelID = parent.ID; + + } ); + + var rawModel = fbxTree.Objects.Model[ modelID.toString() ]; + + var node = { + + modelName: THREE.PropertyBinding.sanitizeNodeName( rawModel.attrName ), + initialPosition: [ 0, 0, 0 ], + initialRotation: [ 0, 0, 0 ], + initialScale: [ 1, 1, 1 ], + transform: self.getModelAnimTransform( rawModel ), + + }; + + // if the animated model is pre rotated, we'll have to apply the pre rotations to every + // animation value as well + if ( 'PreRotation' in rawModel ) node.preRotations = rawModel.PreRotation.value; + if ( 'PostRotation' in rawModel ) node.postRotations = rawModel.PostRotation.value; + + layerCurveNodes[ i ] = node; + + } + + layerCurveNodes[ i ][ curveNode.attr ] = curveNode; + + } else if ( curveNode.curves.morph !== undefined ) { + + if ( layerCurveNodes[ i ] === undefined ) { + + var deformerID; + + connections.get( child.ID ).parents.forEach( function ( parent ) { + + if ( parent.relationship !== undefined ) deformerID = parent.ID; + + } ); + + var morpherID = connections.get( deformerID ).parents[ 0 ].ID; + var geoID = connections.get( morpherID ).parents[ 0 ].ID; + + // assuming geometry is not used in more than one model + var modelID = connections.get( geoID ).parents[ 0 ].ID; + + var rawModel = fbxTree.Objects.Model[ modelID ]; + + var node = { + + modelName: THREE.PropertyBinding.sanitizeNodeName( rawModel.attrName ), + morphName: fbxTree.Objects.Deformer[ deformerID ].attrName, + + }; + + layerCurveNodes[ i ] = node; + + } + + layerCurveNodes[ i ][ curveNode.attr ] = curveNode; + + } + + } + + } ); + + layersMap.set( parseInt( nodeID ), layerCurveNodes ); + + } + + } + + return layersMap; + + }, + + getModelAnimTransform: function ( modelNode ) { + + var transformData = {}; + + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = parseInt( modelNode.RotationOrder.value ); + + if ( 'Lcl_Translation' in modelNode ) transformData.translation = modelNode.Lcl_Translation.value; + if ( 'RotationOffset' in modelNode ) transformData.rotationOffset = modelNode.RotationOffset.value; + + if ( 'Lcl_Rotation' in modelNode ) transformData.rotation = modelNode.Lcl_Rotation.value; + if ( 'PreRotation' in modelNode ) transformData.preRotation = modelNode.PreRotation.value; + + if ( 'PostRotation' in modelNode ) transformData.postRotation = modelNode.PostRotation.value; + + if ( 'Lcl_Scaling' in modelNode ) transformData.scale = modelNode.Lcl_Scaling.value; + + return generateTransform( transformData ); + + }, + + // parse nodes in FBXTree.Objects.AnimationStack. These are the top level node in the animation + // hierarchy. Each Stack node will be used to create a THREE.AnimationClip + parseAnimStacks: function ( layersMap ) { + + var rawStacks = fbxTree.Objects.AnimationStack; + + // connect the stacks (clips) up to the layers + var rawClips = {}; + + for ( var nodeID in rawStacks ) { + + var children = connections.get( parseInt( nodeID ) ).children; + + if ( children.length > 1 ) { + + // it seems like stacks will always be associated with a single layer. But just in case there are files + // where there are multiple layers per stack, we'll display a warning + console.warn( 'THREE.FBXLoader: Encountered an animation stack with multiple layers, this is currently not supported. Ignoring subsequent layers.' ); + + } + + var layer = layersMap.get( children[ 0 ].ID ); + + rawClips[ nodeID ] = { + + name: rawStacks[ nodeID ].attrName, + layer: layer, + + }; + + } + + return rawClips; + + }, + + addClip: function ( rawClip ) { + + var tracks = []; + + var self = this; + rawClip.layer.forEach( function ( rawTracks ) { + + tracks = tracks.concat( self.generateTracks( rawTracks ) ); + + } ); + + return new THREE.AnimationClip( rawClip.name, - 1, tracks ); + + }, + + generateTracks: function ( rawTracks ) { + + var tracks = []; + + var initialPosition = new THREE.Vector3(); + var initialRotation = new THREE.Quaternion(); + var initialScale = new THREE.Vector3(); + + if ( rawTracks.transform ) rawTracks.transform.decompose( initialPosition, initialRotation, initialScale ); + + initialPosition = initialPosition.toArray(); + initialRotation = new THREE.Euler().setFromQuaternion( initialRotation ).toArray(); // todo: euler order + initialScale = initialScale.toArray(); + + if ( rawTracks.T !== undefined && Object.keys( rawTracks.T.curves ).length > 0 ) { + + var positionTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.T.curves, initialPosition, 'position' ); + if ( positionTrack !== undefined ) tracks.push( positionTrack ); + + } + + if ( rawTracks.R !== undefined && Object.keys( rawTracks.R.curves ).length > 0 ) { + + var rotationTrack = this.generateRotationTrack( rawTracks.modelName, rawTracks.R.curves, initialRotation, rawTracks.preRotations, rawTracks.postRotations ); + if ( rotationTrack !== undefined ) tracks.push( rotationTrack ); + + } + + if ( rawTracks.S !== undefined && Object.keys( rawTracks.S.curves ).length > 0 ) { + + var scaleTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.S.curves, initialScale, 'scale' ); + if ( scaleTrack !== undefined ) tracks.push( scaleTrack ); + + } + + if ( rawTracks.DeformPercent !== undefined ) { + + var morphTrack = this.generateMorphTrack( rawTracks ); + if ( morphTrack !== undefined ) tracks.push( morphTrack ); + + } + + return tracks; + + }, + + generateVectorTrack: function ( modelName, curves, initialValue, type ) { + + var times = this.getTimesForAllAxes( curves ); + var values = this.getKeyframeTrackValues( times, curves, initialValue ); + + return new THREE.VectorKeyframeTrack( modelName + '.' + type, times, values ); + + }, + + generateRotationTrack: function ( modelName, curves, initialValue, preRotations, postRotations ) { + + if ( curves.x !== undefined ) { + + this.interpolateRotations( curves.x ); + curves.x.values = curves.x.values.map( THREE.Math.degToRad ); + + } + if ( curves.y !== undefined ) { + + this.interpolateRotations( curves.y ); + curves.y.values = curves.y.values.map( THREE.Math.degToRad ); + + } + if ( curves.z !== undefined ) { + + this.interpolateRotations( curves.z ); + curves.z.values = curves.z.values.map( THREE.Math.degToRad ); + + } + + var times = this.getTimesForAllAxes( curves ); + var values = this.getKeyframeTrackValues( times, curves, initialValue ); + + if ( preRotations !== undefined ) { + + preRotations = preRotations.map( THREE.Math.degToRad ); + preRotations.push( 'ZYX' ); + + preRotations = new THREE.Euler().fromArray( preRotations ); + preRotations = new THREE.Quaternion().setFromEuler( preRotations ); + + } + + if ( postRotations !== undefined ) { + + postRotations = postRotations.map( THREE.Math.degToRad ); + postRotations.push( 'ZYX' ); + + postRotations = new THREE.Euler().fromArray( postRotations ); + postRotations = new THREE.Quaternion().setFromEuler( postRotations ).inverse(); + + } + + var quaternion = new THREE.Quaternion(); + var euler = new THREE.Euler(); + + var quaternionValues = []; + + for ( var i = 0; i < values.length; i += 3 ) { + + euler.set( values[ i ], values[ i + 1 ], values[ i + 2 ], 'ZYX' ); + + quaternion.setFromEuler( euler ); + + if ( preRotations !== undefined ) quaternion.premultiply( preRotations ); + if ( postRotations !== undefined ) quaternion.multiply( postRotations ); + + quaternion.toArray( quaternionValues, ( i / 3 ) * 4 ); + + } + + return new THREE.QuaternionKeyframeTrack( modelName + '.quaternion', times, quaternionValues ); + + }, + + generateMorphTrack: function ( rawTracks ) { + + var curves = rawTracks.DeformPercent.curves.morph; + var values = curves.values.map( function ( val ) { + + return val / 100; + + } ); + + var morphNum = sceneGraph.getObjectByName( rawTracks.modelName ).morphTargetDictionary[ rawTracks.morphName ]; + + return new THREE.NumberKeyframeTrack( rawTracks.modelName + '.morphTargetInfluences[' + morphNum + ']', curves.times, values ); + + }, + + // For all animated objects, times are defined separately for each axis + // Here we'll combine the times into one sorted array without duplicates + getTimesForAllAxes: function ( curves ) { + + var times = []; + + // first join together the times for each axis, if defined + if ( curves.x !== undefined ) times = times.concat( curves.x.times ); + if ( curves.y !== undefined ) times = times.concat( curves.y.times ); + if ( curves.z !== undefined ) times = times.concat( curves.z.times ); + + // then sort them and remove duplicates + times = times.sort( function ( a, b ) { + + return a - b; + + } ).filter( function ( elem, index, array ) { + + return array.indexOf( elem ) == index; + + } ); + + return times; + + }, + + getKeyframeTrackValues: function ( times, curves, initialValue ) { + + var prevValue = initialValue; + + var values = []; + + var xIndex = - 1; + var yIndex = - 1; + var zIndex = - 1; + + times.forEach( function ( time ) { + + if ( curves.x ) xIndex = curves.x.times.indexOf( time ); + if ( curves.y ) yIndex = curves.y.times.indexOf( time ); + if ( curves.z ) zIndex = curves.z.times.indexOf( time ); + + // if there is an x value defined for this frame, use that + if ( xIndex !== - 1 ) { + + var xValue = curves.x.values[ xIndex ]; + values.push( xValue ); + prevValue[ 0 ] = xValue; + + } else { + + // otherwise use the x value from the previous frame + values.push( prevValue[ 0 ] ); + + } + + if ( yIndex !== - 1 ) { + + var yValue = curves.y.values[ yIndex ]; + values.push( yValue ); + prevValue[ 1 ] = yValue; + + } else { + + values.push( prevValue[ 1 ] ); + + } + + if ( zIndex !== - 1 ) { + + var zValue = curves.z.values[ zIndex ]; + values.push( zValue ); + prevValue[ 2 ] = zValue; + + } else { + + values.push( prevValue[ 2 ] ); + + } + + } ); + + return values; + + }, + + // Rotations are defined as Euler angles which can have values of any size + // These will be converted to quaternions which don't support values greater than + // PI, so we'll interpolate large rotations + interpolateRotations: function ( curve ) { + + for ( var i = 1; i < curve.values.length; i ++ ) { + + var initialValue = curve.values[ i - 1 ]; + var valuesSpan = curve.values[ i ] - initialValue; + + var absoluteSpan = Math.abs( valuesSpan ); + + if ( absoluteSpan >= 180 ) { + + var numSubIntervals = absoluteSpan / 180; + + var step = valuesSpan / numSubIntervals; + var nextValue = initialValue + step; + + var initialTime = curve.times[ i - 1 ]; + var timeSpan = curve.times[ i ] - initialTime; + var interval = timeSpan / numSubIntervals; + var nextTime = initialTime + interval; + + var interpolatedTimes = []; + var interpolatedValues = []; + + while ( nextTime < curve.times[ i ] ) { + + interpolatedTimes.push( nextTime ); + nextTime += interval; + + interpolatedValues.push( nextValue ); + nextValue += step; + + } + + curve.times = inject( curve.times, i, interpolatedTimes ); + curve.values = inject( curve.values, i, interpolatedValues ); + + } + + } + + }, + + }; + + // parse an FBX file in ASCII format + function TextParser() {} + + TextParser.prototype = { + + constructor: TextParser, + + getPrevNode: function () { + + return this.nodeStack[ this.currentIndent - 2 ]; + + }, + + getCurrentNode: function () { + + return this.nodeStack[ this.currentIndent - 1 ]; + + }, + + getCurrentProp: function () { + + return this.currentProp; + + }, + + pushStack: function ( node ) { + + this.nodeStack.push( node ); + this.currentIndent += 1; + + }, + + popStack: function () { + + this.nodeStack.pop(); + this.currentIndent -= 1; + + }, + + setCurrentProp: function ( val, name ) { + + this.currentProp = val; + this.currentPropName = name; + + }, + + parse: function ( text ) { + + this.currentIndent = 0; + + this.allNodes = new FBXTree(); + this.nodeStack = []; + this.currentProp = []; + this.currentPropName = ''; + + var self = this; + + var split = text.split( /[\r\n]+/ ); + + split.forEach( function ( line, i ) { + + var matchComment = line.match( /^[\s\t]*;/ ); + var matchEmpty = line.match( /^[\s\t]*$/ ); + + if ( matchComment || matchEmpty ) return; + + var matchBeginning = line.match( '^\\t{' + self.currentIndent + '}(\\w+):(.*){', '' ); + var matchProperty = line.match( '^\\t{' + ( self.currentIndent ) + '}(\\w+):[\\s\\t\\r\\n](.*)' ); + var matchEnd = line.match( '^\\t{' + ( self.currentIndent - 1 ) + '}}' ); + + if ( matchBeginning ) { + + self.parseNodeBegin( line, matchBeginning ); + + } else if ( matchProperty ) { + + self.parseNodeProperty( line, matchProperty, split[ ++ i ] ); + + } else if ( matchEnd ) { + + self.popStack(); + + } else if ( line.match( /^[^\s\t}]/ ) ) { + + // large arrays are split over multiple lines terminated with a ',' character + // if this is encountered the line needs to be joined to the previous line + self.parseNodePropertyContinued( line ); + + } + + } ); + + return this.allNodes; + + }, + + parseNodeBegin: function ( line, property ) { + + var nodeName = property[ 1 ].trim().replace( /^"/, '' ).replace( /"$/, '' ); + + var nodeAttrs = property[ 2 ].split( ',' ).map( function ( attr ) { + + return attr.trim().replace( /^"/, '' ).replace( /"$/, '' ); + + } ); + + var node = { name: nodeName }; + var attrs = this.parseNodeAttr( nodeAttrs ); + + var currentNode = this.getCurrentNode(); + + // a top node + if ( this.currentIndent === 0 ) { + + this.allNodes.add( nodeName, node ); + + } else { // a subnode + + // if the subnode already exists, append it + if ( nodeName in currentNode ) { + + // special case Pose needs PoseNodes as an array + if ( nodeName === 'PoseNode' ) { + + currentNode.PoseNode.push( node ); + + } else if ( currentNode[ nodeName ].id !== undefined ) { + + currentNode[ nodeName ] = {}; + currentNode[ nodeName ][ currentNode[ nodeName ].id ] = currentNode[ nodeName ]; + + } + + if ( attrs.id !== '' ) currentNode[ nodeName ][ attrs.id ] = node; + + } else if ( typeof attrs.id === 'number' ) { + + currentNode[ nodeName ] = {}; + currentNode[ nodeName ][ attrs.id ] = node; + + } else if ( nodeName !== 'Properties70' ) { + + if ( nodeName === 'PoseNode' ) currentNode[ nodeName ] = [ node ]; + else currentNode[ nodeName ] = node; + + } + + } + + if ( typeof attrs.id === 'number' ) node.id = attrs.id; + if ( attrs.name !== '' ) node.attrName = attrs.name; + if ( attrs.type !== '' ) node.attrType = attrs.type; + + this.pushStack( node ); + + }, + + parseNodeAttr: function ( attrs ) { + + var id = attrs[ 0 ]; + + if ( attrs[ 0 ] !== '' ) { + + id = parseInt( attrs[ 0 ] ); + + if ( isNaN( id ) ) { + + id = attrs[ 0 ]; + + } + + } + + var name = '', type = ''; + + if ( attrs.length > 1 ) { + + name = attrs[ 1 ].replace( /^(\w+)::/, '' ); + type = attrs[ 2 ]; + + } + + return { id: id, name: name, type: type }; + + }, + + parseNodeProperty: function ( line, property, contentLine ) { + + var propName = property[ 1 ].replace( /^"/, '' ).replace( /"$/, '' ).trim(); + var propValue = property[ 2 ].replace( /^"/, '' ).replace( /"$/, '' ).trim(); + + // for special case: base64 image data follows "Content: ," line + // Content: , + // "/9j/4RDaRXhpZgAATU0A..." + if ( propName === 'Content' && propValue === ',' ) { + + propValue = contentLine.replace( /"/g, '' ).replace( /,$/, '' ).trim(); + + } + + var currentNode = this.getCurrentNode(); + var parentName = currentNode.name; + + if ( parentName === 'Properties70' ) { + + this.parseNodeSpecialProperty( line, propName, propValue ); + return; + + } + + // Connections + if ( propName === 'C' ) { + + var connProps = propValue.split( ',' ).slice( 1 ); + var from = parseInt( connProps[ 0 ] ); + var to = parseInt( connProps[ 1 ] ); + + var rest = propValue.split( ',' ).slice( 3 ); + + rest = rest.map( function ( elem ) { + + return elem.trim().replace( /^"/, '' ); + + } ); + + propName = 'connections'; + propValue = [ from, to ]; + append( propValue, rest ); + + if ( currentNode[ propName ] === undefined ) { + + currentNode[ propName ] = []; + + } + + } + + // Node + if ( propName === 'Node' ) currentNode.id = propValue; + + // connections + if ( propName in currentNode && Array.isArray( currentNode[ propName ] ) ) { + + currentNode[ propName ].push( propValue ); + + } else { + + if ( propName !== 'a' ) currentNode[ propName ] = propValue; + else currentNode.a = propValue; + + } + + this.setCurrentProp( currentNode, propName ); + + // convert string to array, unless it ends in ',' in which case more will be added to it + if ( propName === 'a' && propValue.slice( - 1 ) !== ',' ) { + + currentNode.a = parseNumberArray( propValue ); + + } + + }, + + parseNodePropertyContinued: function ( line ) { + + var currentNode = this.getCurrentNode(); + + currentNode.a += line; + + // if the line doesn't end in ',' we have reached the end of the property value + // so convert the string to an array + if ( line.slice( - 1 ) !== ',' ) { + + currentNode.a = parseNumberArray( currentNode.a ); + + } + + }, + + // parse "Property70" + parseNodeSpecialProperty: function ( line, propName, propValue ) { + + // split this + // P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1 + // into array like below + // ["Lcl Scaling", "Lcl Scaling", "", "A", "1,1,1" ] + var props = propValue.split( '",' ).map( function ( prop ) { + + return prop.trim().replace( /^\"/, '' ).replace( /\s/, '_' ); + + } ); + + var innerPropName = props[ 0 ]; + var innerPropType1 = props[ 1 ]; + var innerPropType2 = props[ 2 ]; + var innerPropFlag = props[ 3 ]; + var innerPropValue = props[ 4 ]; + + // cast values where needed, otherwise leave as strings + switch ( innerPropType1 ) { + + case 'int': + case 'enum': + case 'bool': + case 'ULongLong': + case 'double': + case 'Number': + case 'FieldOfView': + innerPropValue = parseFloat( innerPropValue ); + break; + + case 'Color': + case 'ColorRGB': + case 'Vector3D': + case 'Lcl_Translation': + case 'Lcl_Rotation': + case 'Lcl_Scaling': + innerPropValue = parseNumberArray( innerPropValue ); + break; + + } + + // CAUTION: these props must append to parent's parent + this.getPrevNode()[ innerPropName ] = { + + 'type': innerPropType1, + 'type2': innerPropType2, + 'flag': innerPropFlag, + 'value': innerPropValue + + }; + + this.setCurrentProp( this.getPrevNode(), innerPropName ); + + }, + + }; + + // Parse an FBX file in Binary format + function BinaryParser() {} + + BinaryParser.prototype = { + + constructor: BinaryParser, + + parse: function ( buffer ) { + + var reader = new BinaryReader( buffer ); + reader.skip( 23 ); // skip magic 23 bytes + + var version = reader.getUint32(); + + console.log( 'THREE.FBXLoader: FBX binary version: ' + version ); + + var allNodes = new FBXTree(); + + while ( ! this.endOfContent( reader ) ) { + + var node = this.parseNode( reader, version ); + if ( node !== null ) allNodes.add( node.name, node ); + + } + + return allNodes; + + }, + + // Check if reader has reached the end of content. + endOfContent: function ( reader ) { + + // footer size: 160bytes + 16-byte alignment padding + // - 16bytes: magic + // - padding til 16-byte alignment (at least 1byte?) + // (seems like some exporters embed fixed 15 or 16bytes?) + // - 4bytes: magic + // - 4bytes: version + // - 120bytes: zero + // - 16bytes: magic + if ( reader.size() % 16 === 0 ) { + + return ( ( reader.getOffset() + 160 + 16 ) & ~ 0xf ) >= reader.size(); + + } else { + + return reader.getOffset() + 160 + 16 >= reader.size(); + + } + + }, + + // recursively parse nodes until the end of the file is reached + parseNode: function ( reader, version ) { + + var node = {}; + + // The first three data sizes depends on version. + var endOffset = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + var numProperties = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + + // note: do not remove this even if you get a linter warning as it moves the buffer forward + var propertyListLen = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + + var nameLen = reader.getUint8(); + var name = reader.getString( nameLen ); + + // Regards this node as NULL-record if endOffset is zero + if ( endOffset === 0 ) return null; + + var propertyList = []; + + for ( var i = 0; i < numProperties; i ++ ) { + + propertyList.push( this.parseProperty( reader ) ); + + } + + // Regards the first three elements in propertyList as id, attrName, and attrType + var id = propertyList.length > 0 ? propertyList[ 0 ] : ''; + var attrName = propertyList.length > 1 ? propertyList[ 1 ] : ''; + var attrType = propertyList.length > 2 ? propertyList[ 2 ] : ''; + + // check if this node represents just a single property + // like (name, 0) set or (name2, [0, 1, 2]) set of {name: 0, name2: [0, 1, 2]} + node.singleProperty = ( numProperties === 1 && reader.getOffset() === endOffset ) ? true : false; + + while ( endOffset > reader.getOffset() ) { + + var subNode = this.parseNode( reader, version ); + + if ( subNode !== null ) this.parseSubNode( name, node, subNode ); + + } + + node.propertyList = propertyList; // raw property list used by parent + + if ( typeof id === 'number' ) node.id = id; + if ( attrName !== '' ) node.attrName = attrName; + if ( attrType !== '' ) node.attrType = attrType; + if ( name !== '' ) node.name = name; + + return node; + + }, + + parseSubNode: function ( name, node, subNode ) { + + // special case: child node is single property + if ( subNode.singleProperty === true ) { + + var value = subNode.propertyList[ 0 ]; + + if ( Array.isArray( value ) ) { + + node[ subNode.name ] = subNode; + + subNode.a = value; + + } else { + + node[ subNode.name ] = value; + + } + + } else if ( name === 'Connections' && subNode.name === 'C' ) { + + var array = []; + + subNode.propertyList.forEach( function ( property, i ) { + + // first Connection is FBX type (OO, OP, etc.). We'll discard these + if ( i !== 0 ) array.push( property ); + + } ); + + if ( node.connections === undefined ) { + + node.connections = []; + + } + + node.connections.push( array ); + + } else if ( subNode.name === 'Properties70' ) { + + var keys = Object.keys( subNode ); + + keys.forEach( function ( key ) { + + node[ key ] = subNode[ key ]; + + } ); + + } else if ( name === 'Properties70' && subNode.name === 'P' ) { + + var innerPropName = subNode.propertyList[ 0 ]; + var innerPropType1 = subNode.propertyList[ 1 ]; + var innerPropType2 = subNode.propertyList[ 2 ]; + var innerPropFlag = subNode.propertyList[ 3 ]; + var innerPropValue; + + if ( innerPropName.indexOf( 'Lcl ' ) === 0 ) innerPropName = innerPropName.replace( 'Lcl ', 'Lcl_' ); + if ( innerPropType1.indexOf( 'Lcl ' ) === 0 ) innerPropType1 = innerPropType1.replace( 'Lcl ', 'Lcl_' ); + + if ( innerPropType1 === 'Color' || innerPropType1 === 'ColorRGB' || innerPropType1 === 'Vector' || innerPropType1 === 'Vector3D' || innerPropType1.indexOf( 'Lcl_' ) === 0 ) { + + innerPropValue = [ + subNode.propertyList[ 4 ], + subNode.propertyList[ 5 ], + subNode.propertyList[ 6 ] + ]; + + } else { + + innerPropValue = subNode.propertyList[ 4 ]; + + } + + // this will be copied to parent, see above + node[ innerPropName ] = { + + 'type': innerPropType1, + 'type2': innerPropType2, + 'flag': innerPropFlag, + 'value': innerPropValue + + }; + + } else if ( node[ subNode.name ] === undefined ) { + + if ( typeof subNode.id === 'number' ) { + + node[ subNode.name ] = {}; + node[ subNode.name ][ subNode.id ] = subNode; + + } else { + + node[ subNode.name ] = subNode; + + } + + } else { + + if ( subNode.name === 'PoseNode' ) { + + if ( ! Array.isArray( node[ subNode.name ] ) ) { + + node[ subNode.name ] = [ node[ subNode.name ] ]; + + } + + node[ subNode.name ].push( subNode ); + + } else if ( node[ subNode.name ][ subNode.id ] === undefined ) { + + node[ subNode.name ][ subNode.id ] = subNode; + + } + + } + + }, + + parseProperty: function ( reader ) { + + var type = reader.getString( 1 ); + + switch ( type ) { + + case 'C': + return reader.getBoolean(); + + case 'D': + return reader.getFloat64(); + + case 'F': + return reader.getFloat32(); + + case 'I': + return reader.getInt32(); + + case 'L': + return reader.getInt64(); + + case 'R': + var length = reader.getUint32(); + return reader.getArrayBuffer( length ); + + case 'S': + var length = reader.getUint32(); + return reader.getString( length ); + + case 'Y': + return reader.getInt16(); + + case 'b': + case 'c': + case 'd': + case 'f': + case 'i': + case 'l': + + var arrayLength = reader.getUint32(); + var encoding = reader.getUint32(); // 0: non-compressed, 1: compressed + var compressedLength = reader.getUint32(); + + if ( encoding === 0 ) { + + switch ( type ) { + + case 'b': + case 'c': + return reader.getBooleanArray( arrayLength ); + + case 'd': + return reader.getFloat64Array( arrayLength ); + + case 'f': + return reader.getFloat32Array( arrayLength ); + + case 'i': + return reader.getInt32Array( arrayLength ); + + case 'l': + return reader.getInt64Array( arrayLength ); + + } + + } + + if ( typeof Zlib === 'undefined' ) { + + console.error( 'THREE.FBXLoader: External library Inflate.min.js required, obtain or import from https://github.com/imaya/zlib.js' ); + + } + + var inflate = new Zlib.Inflate( new Uint8Array( reader.getArrayBuffer( compressedLength ) ) ); // eslint-disable-line no-undef + var reader2 = new BinaryReader( inflate.decompress().buffer ); + + switch ( type ) { + + case 'b': + case 'c': + return reader2.getBooleanArray( arrayLength ); + + case 'd': + return reader2.getFloat64Array( arrayLength ); + + case 'f': + return reader2.getFloat32Array( arrayLength ); + + case 'i': + return reader2.getInt32Array( arrayLength ); + + case 'l': + return reader2.getInt64Array( arrayLength ); + + } + + default: + throw new Error( 'THREE.FBXLoader: Unknown property type ' + type ); + + } + + } + + }; + + function BinaryReader( buffer, littleEndian ) { + + this.dv = new DataView( buffer ); + this.offset = 0; + this.littleEndian = ( littleEndian !== undefined ) ? littleEndian : true; + + } + + BinaryReader.prototype = { + + constructor: BinaryReader, + + getOffset: function () { + + return this.offset; + + }, + + size: function () { + + return this.dv.buffer.byteLength; + + }, + + skip: function ( length ) { + + this.offset += length; + + }, + + // seems like true/false representation depends on exporter. + // true: 1 or 'Y'(=0x59), false: 0 or 'T'(=0x54) + // then sees LSB. + getBoolean: function () { + + return ( this.getUint8() & 1 ) === 1; + + }, + + getBooleanArray: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getBoolean() ); + + } + + return a; + + }, + + getUint8: function () { + + var value = this.dv.getUint8( this.offset ); + this.offset += 1; + return value; + + }, + + getInt16: function () { + + var value = this.dv.getInt16( this.offset, this.littleEndian ); + this.offset += 2; + return value; + + }, + + getInt32: function () { + + var value = this.dv.getInt32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getInt32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt32() ); + + } + + return a; + + }, + + getUint32: function () { + + var value = this.dv.getUint32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + // JavaScript doesn't support 64-bit integer so calculate this here + // 1 << 32 will return 1 so using multiply operation instead here. + // There's a possibility that this method returns wrong value if the value + // is out of the range between Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER. + // TODO: safely handle 64-bit integer + getInt64: function () { + + var low, high; + + if ( this.littleEndian ) { + + low = this.getUint32(); + high = this.getUint32(); + + } else { + + high = this.getUint32(); + low = this.getUint32(); + + } + + // calculate negative value + if ( high & 0x80000000 ) { + + high = ~ high & 0xFFFFFFFF; + low = ~ low & 0xFFFFFFFF; + + if ( low === 0xFFFFFFFF ) high = ( high + 1 ) & 0xFFFFFFFF; + + low = ( low + 1 ) & 0xFFFFFFFF; + + return - ( high * 0x100000000 + low ); + + } + + return high * 0x100000000 + low; + + }, + + getInt64Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt64() ); + + } + + return a; + + }, + + // Note: see getInt64() comment + getUint64: function () { + + var low, high; + + if ( this.littleEndian ) { + + low = this.getUint32(); + high = this.getUint32(); + + } else { + + high = this.getUint32(); + low = this.getUint32(); + + } + + return high * 0x100000000 + low; + + }, + + getFloat32: function () { + + var value = this.dv.getFloat32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getFloat32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getFloat32() ); + + } + + return a; + + }, + + getFloat64: function () { + + var value = this.dv.getFloat64( this.offset, this.littleEndian ); + this.offset += 8; + return value; + + }, + + getFloat64Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getFloat64() ); + + } + + return a; + + }, + + getArrayBuffer: function ( size ) { + + var value = this.dv.buffer.slice( this.offset, this.offset + size ); + this.offset += size; + return value; + + }, + + getString: function ( size ) { + + // note: safari 9 doesn't support Uint8Array.indexOf; create intermediate array instead + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a[ i ] = this.getUint8(); + + } + + var nullByte = a.indexOf( 0 ); + if ( nullByte >= 0 ) a = a.slice( 0, nullByte ); + + return THREE.LoaderUtils.decodeText( new Uint8Array( a ) ); + + } + + }; + + // FBXTree holds a representation of the FBX data, returned by the TextParser ( FBX ASCII format) + // and BinaryParser( FBX Binary format) + function FBXTree() {} + + FBXTree.prototype = { + + constructor: FBXTree, + + add: function ( key, val ) { + + this[ key ] = val; + + }, + + }; + + // ************** UTILITY FUNCTIONS ************** + + function isFbxFormatBinary( buffer ) { + + var CORRECT = 'Kaydara FBX Binary \0'; + + return buffer.byteLength >= CORRECT.length && CORRECT === convertArrayBufferToString( buffer, 0, CORRECT.length ); + + } + + function isFbxFormatASCII( text ) { + + var CORRECT = [ 'K', 'a', 'y', 'd', 'a', 'r', 'a', '\\', 'F', 'B', 'X', '\\', 'B', 'i', 'n', 'a', 'r', 'y', '\\', '\\' ]; + + var cursor = 0; + + function read( offset ) { + + var result = text[ offset - 1 ]; + text = text.slice( cursor + offset ); + cursor ++; + return result; + + } + + for ( var i = 0; i < CORRECT.length; ++ i ) { + + var num = read( 1 ); + if ( num === CORRECT[ i ] ) { + + return false; + + } + + } + + return true; + + } + + function getFbxVersion( text ) { + + var versionRegExp = /FBXVersion: (\d+)/; + var match = text.match( versionRegExp ); + if ( match ) { + + var version = parseInt( match[ 1 ] ); + return version; + + } + throw new Error( 'THREE.FBXLoader: Cannot find the version number for the file given.' ); + + } + + // Converts FBX ticks into real time seconds. + function convertFBXTimeToSeconds( time ) { + + return time / 46186158000; + + } + + var dataArray = []; + + // extracts the data from the correct position in the FBX array based on indexing type + function getData( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) { + + var index; + + switch ( infoObject.mappingType ) { + + case 'ByPolygonVertex' : + index = polygonVertexIndex; + break; + case 'ByPolygon' : + index = polygonIndex; + break; + case 'ByVertice' : + index = vertexIndex; + break; + case 'AllSame' : + index = infoObject.indices[ 0 ]; + break; + default : + console.warn( 'THREE.FBXLoader: unknown attribute mapping type ' + infoObject.mappingType ); + + } + + if ( infoObject.referenceType === 'IndexToDirect' ) index = infoObject.indices[ index ]; + + var from = index * infoObject.dataSize; + var to = from + infoObject.dataSize; + + return slice( dataArray, infoObject.buffer, from, to ); + + } + + var tempMat = new THREE.Matrix4(); + var tempEuler = new THREE.Euler(); + var tempVec = new THREE.Vector3(); + var translation = new THREE.Vector3(); + var rotation = new THREE.Matrix4(); + + // generate transformation from FBX transform data + // ref: https://help.autodesk.com/view/FBX/2017/ENU/?guid=__files_GUID_10CDD63C_79C1_4F2D_BB28_AD2BE65A02ED_htm + // transformData = { + // eulerOrder: int, + // translation: [], + // rotationOffset: [], + // preRotation + // rotation + // postRotation + // scale + // } + // all entries are optional + function generateTransform( transformData ) { + + var transform = new THREE.Matrix4(); + translation.set( 0, 0, 0 ); + rotation.identity(); + + var order = ( transformData.eulerOrder ) ? getEulerOrder( transformData.eulerOrder ) : getEulerOrder( 0 ); + + if ( transformData.translation ) translation.fromArray( transformData.translation ); + if ( transformData.rotationOffset ) translation.add( tempVec.fromArray( transformData.rotationOffset ) ); + + if ( transformData.rotation ) { + + var array = transformData.rotation.map( THREE.Math.degToRad ); + array.push( order ); + rotation.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + } + + if ( transformData.preRotation ) { + + var array = transformData.preRotation.map( THREE.Math.degToRad ); + array.push( order ); + tempMat.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + rotation.premultiply( tempMat ); + + } + + if ( transformData.postRotation ) { + + var array = transformData.postRotation.map( THREE.Math.degToRad ); + array.push( order ); + tempMat.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + tempMat.getInverse( tempMat ); + + rotation.multiply( tempMat ); + + } + + if ( transformData.scale ) transform.scale( tempVec.fromArray( transformData.scale ) ); + + transform.setPosition( translation ); + transform.multiply( rotation ); + + return transform; + + } + + // Returns the three.js intrinsic Euler order corresponding to FBX extrinsic Euler order + // ref: http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_euler_html + function getEulerOrder( order ) { + + var enums = [ + 'ZYX', // -> XYZ extrinsic + 'YZX', // -> XZY extrinsic + 'XZY', // -> YZX extrinsic + 'ZXY', // -> YXZ extrinsic + 'YXZ', // -> ZXY extrinsic + 'XYZ', // -> ZYX extrinsic + //'SphericXYZ', // not possible to support + ]; + + if ( order === 6 ) { + + console.warn( 'THREE.FBXLoader: unsupported Euler Order: Spherical XYZ. Animations and rotations may be incorrect.' ); + return enums[ 0 ]; + + } + + return enums[ order ]; + + } + + // Parses comma separated list of numbers and returns them an array. + // Used internally by the TextParser + function parseNumberArray( value ) { + + var array = value.split( ',' ).map( function ( val ) { + + return parseFloat( val ); + + } ); + + return array; + + } + + function convertArrayBufferToString( buffer, from, to ) { + + if ( from === undefined ) from = 0; + if ( to === undefined ) to = buffer.byteLength; + + return THREE.LoaderUtils.decodeText( new Uint8Array( buffer, from, to ) ); + + } + + function append( a, b ) { + + for ( var i = 0, j = a.length, l = b.length; i < l; i ++, j ++ ) { + + a[ j ] = b[ i ]; + + } + + } + + function slice( a, b, from, to ) { + + for ( var i = from, j = 0; i < to; i ++, j ++ ) { + + a[ j ] = b[ i ]; + + } + + return a; + + } + + // inject array a2 into array a1 at index + function inject( a1, index, a2 ) { + + return a1.slice( 0, index ).concat( a2 ).concat( a1.slice( index ) ); + + } + + return FBXLoader; + +} )(); diff --git a/daydream/public/libs/FBXLoader_old.js b/daydream/public/libs/FBXLoader_old.js new file mode 100644 index 000000000..320605877 --- /dev/null +++ b/daydream/public/libs/FBXLoader_old.js @@ -0,0 +1,3891 @@ +/** + * @author Kyle-Larson https://github.com/Kyle-Larson + * @author Takahiro https://github.com/takahirox + * @author Lewy Blue https://github.com/looeee + * + * Loader loads FBX file and generates Group representing FBX scene. + * Requires FBX file to be >= 7.0 and in ASCII or >= 6400 in Binary format + * Versions lower than this may load but will probably have errors + * + * Needs Support: + * Morph normals / blend shape normals + * Animation tracks for morph targets + * + * Euler rotation order + * + * FBX format references: + * https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure + * http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_index_html (C++ SDK reference) + * + * Binary format specification: + * https://code.blender.org/2013/08/fbx-binary-file-format-specification/ + */ + + +( function () { + + THREE.FBXLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + }; + + Object.assign( THREE.FBXLoader.prototype, { + + load: function ( url, onLoad, onProgress, onError ) { + + var self = this; + + var resourceDirectory = THREE.LoaderUtils.extractUrlBase( url ); + + var loader = new THREE.FileLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( buffer ) { + + try { + + var scene = self.parse( buffer, resourceDirectory ); + onLoad( scene ); + + } catch ( error ) { + + window.setTimeout( function () { + + if ( onError ) onError( error ); + + self.manager.itemError( url ); + + }, 0 ); + + } + + }, onProgress, onError ); + + }, + + parse: function ( FBXBuffer, resourceDirectory ) { + + var FBXTree; + + if ( isFbxFormatBinary( FBXBuffer ) ) { + + FBXTree = new BinaryParser().parse( FBXBuffer ); + + } else { + + var FBXText = convertArrayBufferToString( FBXBuffer ); + + if ( ! isFbxFormatASCII( FBXText ) ) { + + throw new Error( 'THREE.FBXLoader: Unknown format.' ); + + } + + if ( getFbxVersion( FBXText ) < 7000 ) { + + throw new Error( 'THREE.FBXLoader: FBX version not supported, FileVersion: ' + getFbxVersion( FBXText ) ); + + } + + FBXTree = new TextParser().parse( FBXText ); + + } + + // console.log( FBXTree ); + + var connections = parseConnections( FBXTree ); + var images = parseImages( FBXTree ); + var textures = parseTextures( FBXTree, new THREE.TextureLoader( this.manager ).setPath( resourceDirectory ), images, connections ); + var materials = parseMaterials( FBXTree, textures, connections ); + var deformers = parseDeformers( FBXTree, connections ); + var geometryMap = parseGeometries( FBXTree, connections, deformers ); + var sceneGraph = parseScene( FBXTree, connections, deformers.skeletons, geometryMap, materials ); + + return sceneGraph; + + } + + } ); + + // Parses FBXTree.Connections which holds parent-child connections between objects (e.g. material -> texture, model->geometry ) + // and details the connection type + function parseConnections( FBXTree ) { + + var connectionMap = new Map(); + + if ( 'Connections' in FBXTree ) { + + var rawConnections = FBXTree.Connections.connections; + + rawConnections.forEach( function ( rawConnection ) { + + var fromID = rawConnection[ 0 ]; + var toID = rawConnection[ 1 ]; + var relationship = rawConnection[ 2 ]; + + if ( ! connectionMap.has( fromID ) ) { + + connectionMap.set( fromID, { + parents: [], + children: [] + } ); + + } + + var parentRelationship = { ID: toID, relationship: relationship }; + connectionMap.get( fromID ).parents.push( parentRelationship ); + + if ( ! connectionMap.has( toID ) ) { + + connectionMap.set( toID, { + parents: [], + children: [] + } ); + + } + + var childRelationship = { ID: fromID, relationship: relationship }; + connectionMap.get( toID ).children.push( childRelationship ); + + } ); + + } + + return connectionMap; + + } + + // Parse FBXTree.Objects.Video for embedded image data + // These images are connected to textures in FBXTree.Objects.Textures + // via FBXTree.Connections. + function parseImages( FBXTree ) { + + var images = {}; + var blobs = {}; + + if ( 'Video' in FBXTree.Objects ) { + + var videoNodes = FBXTree.Objects.Video; + + for ( var nodeID in videoNodes ) { + + var videoNode = videoNodes[ nodeID ]; + + var id = parseInt( nodeID ); + + images[ id ] = videoNode.RelativeFilename || videoNode.Filename; + + // raw image data is in videoNode.Content + if ( 'Content' in videoNode ) { + + var arrayBufferContent = ( videoNode.Content instanceof ArrayBuffer ) && ( videoNode.Content.byteLength > 0 ); + var base64Content = ( typeof videoNode.Content === 'string' ) && ( videoNode.Content !== '' ); + + if ( arrayBufferContent || base64Content ) { + + var image = parseImage( videoNodes[ nodeID ] ); + + blobs[ videoNode.RelativeFilename || videoNode.Filename ] = image; + + } + + } + + } + + } + + for ( var id in images ) { + + var filename = images[ id ]; + + if ( blobs[ filename ] !== undefined ) images[ id ] = blobs[ filename ]; + else images[ id ] = images[ id ].split( '\\' ).pop(); + + } + + return images; + + } + + // Parse embedded image data in FBXTree.Video.Content + function parseImage( videoNode ) { + + var content = videoNode.Content; + var fileName = videoNode.RelativeFilename || videoNode.Filename; + var extension = fileName.slice( fileName.lastIndexOf( '.' ) + 1 ).toLowerCase(); + + var type; + + switch ( extension ) { + + case 'bmp': + + type = 'image/bmp'; + break; + + case 'jpg': + case 'jpeg': + + type = 'image/jpeg'; + break; + + case 'png': + + type = 'image/png'; + break; + + case 'tif': + + type = 'image/tiff'; + break; + + case 'tga': + + if ( typeof THREE.TGALoader !== 'function' ) { + + console.warn( 'FBXLoader: THREE.TGALoader is required to load TGA textures' ); + return; + + } else { + + if ( THREE.Loader.Handlers.get( '.tga' ) === null ) { + + THREE.Loader.Handlers.add( /\.tga$/i, new THREE.TGALoader() ); + + } + + type = 'image/tga'; + break; + + } + + default: + + console.warn( 'FBXLoader: Image type "' + extension + '" is not supported.' ); + return; + + } + + if ( typeof content === 'string' ) { // ASCII format + + return 'data:' + type + ';base64,' + content; + + } else { // Binary Format + + var array = new Uint8Array( content ); + return window.URL.createObjectURL( new Blob( [ array ], { type: type } ) ); + + } + + } + + // Parse nodes in FBXTree.Objects.Texture + // These contain details such as UV scaling, cropping, rotation etc and are connected + // to images in FBXTree.Objects.Video + function parseTextures( FBXTree, loader, images, connections ) { + + var textureMap = new Map(); + + if ( 'Texture' in FBXTree.Objects ) { + + var textureNodes = FBXTree.Objects.Texture; + for ( var nodeID in textureNodes ) { + + var texture = parseTexture( textureNodes[ nodeID ], loader, images, connections ); + textureMap.set( parseInt( nodeID ), texture ); + + } + + } + + return textureMap; + + } + + // Parse individual node in FBXTree.Objects.Texture + function parseTexture( textureNode, loader, images, connections ) { + + var texture = loadTexture( textureNode, loader, images, connections ); + + texture.ID = textureNode.id; + + texture.name = textureNode.attrName; + + var wrapModeU = textureNode.WrapModeU; + var wrapModeV = textureNode.WrapModeV; + + var valueU = wrapModeU !== undefined ? wrapModeU.value : 0; + var valueV = wrapModeV !== undefined ? wrapModeV.value : 0; + + // http://download.autodesk.com/us/fbx/SDKdocs/FBX_SDK_Help/files/fbxsdkref/class_k_fbx_texture.html#889640e63e2e681259ea81061b85143a + // 0: repeat(default), 1: clamp + + texture.wrapS = valueU === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + texture.wrapT = valueV === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + + if ( 'Scaling' in textureNode ) { + + var values = textureNode.Scaling.value; + + texture.repeat.x = values[ 0 ]; + texture.repeat.y = values[ 1 ]; + + } + + return texture; + + } + + // load a texture specified as a blob or data URI, or via an external URL using THREE.TextureLoader + function loadTexture( textureNode, loader, images, connections ) { + + var fileName; + + var currentPath = loader.path; + + var children = connections.get( textureNode.id ).children; + + if ( children !== undefined && children.length > 0 && images[ children[ 0 ].ID ] !== undefined ) { + + fileName = images[ children[ 0 ].ID ]; + + if ( fileName.indexOf( 'blob:' ) === 0 || fileName.indexOf( 'data:' ) === 0 ) { + + loader.setPath( undefined ); + + } + + } + + var texture; + + if ( textureNode.FileName.slice( -3 ).toLowerCase() === 'tga' ) { + + texture = THREE.Loader.Handlers.get( '.tga' ).load( fileName ); + + } else { + + texture = loader.load( fileName ); + + } + + loader.setPath( currentPath ); + + return texture; + + } + + // Parse nodes in FBXTree.Objects.Material + function parseMaterials( FBXTree, textureMap, connections ) { + + var materialMap = new Map(); + + if ( 'Material' in FBXTree.Objects ) { + + var materialNodes = FBXTree.Objects.Material; + + for ( var nodeID in materialNodes ) { + + var material = parseMaterial( FBXTree, materialNodes[ nodeID ], textureMap, connections ); + + if ( material !== null ) materialMap.set( parseInt( nodeID ), material ); + + } + + } + + return materialMap; + + } + + // Parse single node in FBXTree.Objects.Material + // Materials are connected to texture maps in FBXTree.Objects.Textures + // FBX format currently only supports Lambert and Phong shading models + function parseMaterial( FBXTree, materialNode, textureMap, connections ) { + + var ID = materialNode.id; + var name = materialNode.attrName; + var type = materialNode.ShadingModel; + + //Case where FBX wraps shading model in property object. + if ( typeof type === 'object' ) { + + type = type.value; + + } + + // Ignore unused materials which don't have any connections. + if ( ! connections.has( ID ) ) return null; + + var parameters = parseParameters( FBXTree, materialNode, textureMap, ID, connections ); + + var material; + + switch ( type.toLowerCase() ) { + + case 'phong': + material = new THREE.MeshPhongMaterial(); + break; + case 'lambert': + material = new THREE.MeshLambertMaterial(); + break; + default: + console.warn( 'THREE.FBXLoader: unknown material type "%s". Defaulting to MeshPhongMaterial.', type ); + material = new THREE.MeshPhongMaterial( { color: 0x3300ff } ); + break; + + } + + material.setValues( parameters ); + material.name = name; + + return material; + + } + + // Parse FBX material and return parameters suitable for a three.js material + // Also parse the texture map and return any textures associated with the material + function parseParameters( FBXTree, properties, textureMap, ID, connections ) { + + var parameters = {}; + + if ( properties.BumpFactor ) { + + parameters.bumpScale = properties.BumpFactor.value; + + } + if ( properties.Diffuse ) { + + parameters.color = new THREE.Color().fromArray( properties.Diffuse.value ); + + } else if ( properties.DiffuseColor && properties.DiffuseColor.type === 'Color' ) { + + // The blender exporter exports diffuse here instead of in properties.Diffuse + parameters.color = new THREE.Color().fromArray( properties.DiffuseColor.value ); + + } + if ( properties.DisplacementFactor ) { + + parameters.displacementScale = properties.DisplacementFactor.value; + + } + if ( properties.Emissive ) { + + parameters.emissive = new THREE.Color().fromArray( properties.Emissive.value ); + + } else if ( properties.EmissiveColor && properties.EmissiveColor.type === 'Color' ) { + + // The blender exporter exports emissive color here instead of in properties.Emissive + parameters.emissive = new THREE.Color().fromArray( properties.EmissiveColor.value ); + + } + if ( properties.EmissiveFactor ) { + + parameters.emissiveIntensity = parseFloat( properties.EmissiveFactor.value ); + + } + if ( properties.Opacity ) { + + parameters.opacity = parseFloat( properties.Opacity.value ); + + } + if ( parameters.opacity < 1.0 ) { + + parameters.transparent = true; + + } + if ( properties.ReflectionFactor ) { + + parameters.reflectivity = properties.ReflectionFactor.value; + + } + if ( properties.Shininess ) { + + parameters.shininess = properties.Shininess.value; + + } + if ( properties.Specular ) { + + parameters.specular = new THREE.Color().fromArray( properties.Specular.value ); + + } else if ( properties.SpecularColor && properties.SpecularColor.type === 'Color' ) { + + // The blender exporter exports specular color here instead of in properties.Specular + parameters.specular = new THREE.Color().fromArray( properties.SpecularColor.value ); + + } + + connections.get( ID ).children.forEach( function ( child ) { + + var type = child.relationship; + + switch ( type ) { + + case 'Bump': + parameters.bumpMap = textureMap.get( child.ID ); + break; + + case 'DiffuseColor': + parameters.map = getTexture( FBXTree, textureMap, child.ID, connections ); + break; + + case 'DisplacementColor': + parameters.displacementMap = getTexture( FBXTree, textureMap, child.ID, connections ); + break; + + + case 'EmissiveColor': + parameters.emissiveMap = getTexture( FBXTree, textureMap, child.ID, connections ); + break; + + case 'NormalMap': + parameters.normalMap = getTexture( FBXTree, textureMap, child.ID, connections ); + break; + + case 'ReflectionColor': + parameters.envMap = getTexture( FBXTree, textureMap, child.ID, connections ); + parameters.envMap.mapping = THREE.EquirectangularReflectionMapping; + break; + + case 'SpecularColor': + parameters.specularMap = getTexture( FBXTree, textureMap, child.ID, connections ); + break; + + case 'TransparentColor': + parameters.alphaMap = getTexture( FBXTree, textureMap, child.ID, connections ); + parameters.transparent = true; + break; + + case 'AmbientColor': + case 'ShininessExponent': // AKA glossiness map + case 'SpecularFactor': // AKA specularLevel + case 'VectorDisplacementColor': // NOTE: Seems to be a copy of DisplacementColor + default: + console.warn( 'THREE.FBXLoader: %s map is not supported in three.js, skipping texture.', type ); + break; + + } + + } ); + + return parameters; + + } + + // get a texture from the textureMap for use by a material. + function getTexture( FBXTree, textureMap, id, connections ) { + + // if the texture is a layered texture, just use the first layer and issue a warning + if ( 'LayeredTexture' in FBXTree.Objects && id in FBXTree.Objects.LayeredTexture ) { + + console.warn( 'THREE.FBXLoader: layered textures are not supported in three.js. Discarding all but first layer.' ); + id = connections.get( id ).children[ 0 ].ID; + + } + + return textureMap.get( id ); + + } + + // Parse nodes in FBXTree.Objects.Deformer + // Deformer node can contain skinning or Vertex Cache animation data, however only skinning is supported here + // Generates map of Skeleton-like objects for use later when generating and binding skeletons. + function parseDeformers( FBXTree, connections ) { + + var skeletons = {}; + var morphTargets = {}; + + if ( 'Deformer' in FBXTree.Objects ) { + + var DeformerNodes = FBXTree.Objects.Deformer; + + for ( var nodeID in DeformerNodes ) { + + var deformerNode = DeformerNodes[ nodeID ]; + + var relationships = connections.get( parseInt( nodeID ) ); + + if ( deformerNode.attrType === 'Skin' ) { + + var skeleton = parseSkeleton( relationships, DeformerNodes ); + skeleton.ID = nodeID; + + if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: skeleton attached to more than one geometry is not supported.' ); + skeleton.geometryID = relationships.parents[ 0 ].ID; + + skeletons[ nodeID ] = skeleton; + + } else if ( deformerNode.attrType === 'BlendShape' ) { + + var morphTarget = { + id: nodeID, + }; + + morphTarget.rawTargets = parseMorphTargets( relationships, deformerNode, DeformerNodes, connections, FBXTree ); + morphTarget.id = nodeID; + + if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: morph target attached to more than one geometry is not supported.' ); + morphTarget.parentGeoID = relationships.parents[ 0 ].ID; + + morphTargets[ nodeID ] = morphTarget; + + } + + } + + } + + return { + + skeletons: skeletons, + morphTargets: morphTargets, + + }; + + } + + // Parse single nodes in FBXTree.Objects.Deformer + // The top level skeleton node has type 'Skin' and sub nodes have type 'Cluster' + // Each skin node represents a skeleton and each cluster node represents a bone + function parseSkeleton( connections, deformerNodes ) { + + var rawBones = []; + + connections.children.forEach( function ( child ) { + + var boneNode = deformerNodes[ child.ID ]; + + if ( boneNode.attrType !== 'Cluster' ) return; + + var rawBone = { + + ID: child.ID, + indices: [], + weights: [], + transform: new THREE.Matrix4().fromArray( boneNode.Transform.a ), + transformLink: new THREE.Matrix4().fromArray( boneNode.TransformLink.a ), + linkMode: boneNode.Mode, + + }; + + if ( 'Indexes' in boneNode ) { + + rawBone.indices = boneNode.Indexes.a; + rawBone.weights = boneNode.Weights.a; + + } + + rawBones.push( rawBone ); + + } ); + + return { + + rawBones: rawBones, + bones: [] + + }; + + } + + // The top level morph deformer node has type "BlendShape" and sub nodes have type "BlendShapeChannel" + function parseMorphTargets( relationships, deformerNode, deformerNodes, connections ) { + + var rawMorphTargets = []; + + for ( var i = 0; i < relationships.children.length; i ++ ) { + + if ( i === 8 ) { + + console.warn( 'FBXLoader: maximum of 8 morph targets supported. Ignoring additional targets.' ); + + break; + + } + + var child = relationships.children[ i ]; + + var morphTargetNode = deformerNodes[ child.ID ]; + + var rawMorphTarget = { + + name: morphTargetNode.attrName, + initialWeight: morphTargetNode.DeformPercent, + id: morphTargetNode.id, + fullWeights: morphTargetNode.FullWeights.a + + }; + + if ( morphTargetNode.attrType !== 'BlendShapeChannel' ) return; + + var targetRelationships = connections.get( parseInt( child.ID ) ); + + targetRelationships.children.forEach( function ( child ) { + + if ( child.relationship === 'DeformPercent' ) { + + // TODO: animation of morph targets is currently unsupported + rawMorphTarget.weightCurveID = child.ID; + // weightCurve = FBXTree.Objects.AnimationCurveNode[ weightCurveID ]; + + } else { + + rawMorphTarget.geoID = child.ID; + // morphGeo = FBXTree.Objects.Geometry[ geoID ]; + + } + + } ); + + rawMorphTargets.push( rawMorphTarget ); + + } + + return rawMorphTargets; + + } + + // Parse nodes in FBXTree.Objects.Geometry + function parseGeometries( FBXTree, connections, deformers ) { + + var geometryMap = new Map(); + + if ( 'Geometry' in FBXTree.Objects ) { + + var geoNodes = FBXTree.Objects.Geometry; + + for ( var nodeID in geoNodes ) { + + var relationships = connections.get( parseInt( nodeID ) ); + var geo = parseGeometry( FBXTree, relationships, geoNodes[ nodeID ], deformers ); + + geometryMap.set( parseInt( nodeID ), geo ); + + } + + } + + return geometryMap; + + } + + // Parse single node in FBXTree.Objects.Geometry + function parseGeometry( FBXTree, relationships, geoNode, deformers ) { + + switch ( geoNode.attrType ) { + + case 'Mesh': + return parseMeshGeometry( FBXTree, relationships, geoNode, deformers ); + break; + + case 'NurbsCurve': + return parseNurbsGeometry( geoNode ); + break; + + } + + } + + // Parse single node mesh geometry in FBXTree.Objects.Geometry + function parseMeshGeometry( FBXTree, relationships, geoNode, deformers ) { + + var skeletons = deformers.skeletons; + var morphTargets = deformers.morphTargets; + + var modelNodes = relationships.parents.map( function ( parent ) { + + return FBXTree.Objects.Model[ parent.ID ]; + + } ); + + // don't create geometry if it is not associated with any models + if ( modelNodes.length === 0 ) return; + + var skeleton = relationships.children.reduce( function ( skeleton, child ) { + + if ( skeletons[ child.ID ] !== undefined ) skeleton = skeletons[ child.ID ]; + + return skeleton; + + }, null ); + + var morphTarget = relationships.children.reduce( function ( morphTarget, child ) { + + if ( morphTargets[ child.ID ] !== undefined ) morphTarget = morphTargets[ child.ID ]; + + return morphTarget; + + }, null ); + + var preTransform = new THREE.Matrix4(); + + // TODO: if there is more than one model associated with the geometry, AND the models have + // different geometric transforms, then this will cause problems + // if ( modelNodes.length > 1 ) { } + + // For now just assume one model and get the preRotations from that + var modelNode = modelNodes[ 0 ]; + + if ( 'GeometricRotation' in modelNode ) { + + var array = modelNode.GeometricRotation.value.map( THREE.Math.degToRad ); + array[ 3 ] = 'ZYX'; + + preTransform.makeRotationFromEuler( new THREE.Euler().fromArray( array ) ); + + } + + if ( 'GeometricTranslation' in modelNode ) { + + preTransform.setPosition( new THREE.Vector3().fromArray( modelNode.GeometricTranslation.value ) ); + + } + + if ( 'GeometricScaling' in modelNode ) { + + preTransform.scale( new THREE.Vector3().fromArray( modelNode.GeometricScaling.value ) ); + + } + + return genGeometry( FBXTree, geoNode, skeleton, morphTarget, preTransform ); + + } + + // Generate a THREE.BufferGeometry from a node in FBXTree.Objects.Geometry + function genGeometry( FBXTree, geoNode, skeleton, morphTarget, preTransform ) { + + var geo = new THREE.BufferGeometry(); + if ( geoNode.attrName ) geo.name = geoNode.attrName; + + var geoInfo = getGeoInfo( geoNode, skeleton ); + + var buffers = genBuffers( geoInfo ); + + var positionAttribute = new THREE.Float32BufferAttribute( buffers.vertex, 3 ); + + preTransform.applyToBufferAttribute( positionAttribute ); + + geo.addAttribute( 'position', positionAttribute ); + + if ( buffers.colors.length > 0 ) { + + geo.addAttribute( 'color', new THREE.Float32BufferAttribute( buffers.colors, 3 ) ); + + } + + if ( skeleton ) { + + geo.addAttribute( 'skinIndex', new THREE.Uint16BufferAttribute( buffers.weightsIndices, 4 ) ); + + geo.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( buffers.vertexWeights, 4 ) ); + + // used later to bind the skeleton to the model + geo.FBX_Deformer = skeleton; + + } + + if ( buffers.normal.length > 0 ) { + + var normalAttribute = new THREE.Float32BufferAttribute( buffers.normal, 3 ); + + var normalMatrix = new THREE.Matrix3().getNormalMatrix( preTransform ); + normalMatrix.applyToBufferAttribute( normalAttribute ); + + geo.addAttribute( 'normal', normalAttribute ); + + } + + buffers.uvs.forEach( function ( uvBuffer, i ) { + + // subsequent uv buffers are called 'uv1', 'uv2', ... + var name = 'uv' + ( i + 1 ).toString(); + + // the first uv buffer is just called 'uv' + if ( i === 0 ) { + + name = 'uv'; + + } + + geo.addAttribute( name, new THREE.Float32BufferAttribute( buffers.uvs[ i ], 2 ) ); + + } ); + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + // Convert the material indices of each vertex into rendering groups on the geometry. + var prevMaterialIndex = buffers.materialIndex[ 0 ]; + var startIndex = 0; + + buffers.materialIndex.forEach( function ( currentIndex, i ) { + + if ( currentIndex !== prevMaterialIndex ) { + + geo.addGroup( startIndex, i - startIndex, prevMaterialIndex ); + + prevMaterialIndex = currentIndex; + startIndex = i; + + } + + } ); + + // the loop above doesn't add the last group, do that here. + if ( geo.groups.length > 0 ) { + + var lastGroup = geo.groups[ geo.groups.length - 1 ]; + var lastIndex = lastGroup.start + lastGroup.count; + + if ( lastIndex !== buffers.materialIndex.length ) { + + geo.addGroup( lastIndex, buffers.materialIndex.length - lastIndex, prevMaterialIndex ); + + } + + } + + // case where there are multiple materials but the whole geometry is only + // using one of them + if ( geo.groups.length === 0 ) { + + geo.addGroup( 0, buffers.materialIndex.length, buffers.materialIndex[ 0 ] ); + + } + + } + + addMorphTargets( FBXTree, geo, geoNode, morphTarget, preTransform ); + + return geo; + + } + + function getGeoInfo( geoNode, skeleton ) { + + var geoInfo = {}; + + geoInfo.vertexPositions = ( geoNode.Vertices !== undefined ) ? geoNode.Vertices.a : []; + geoInfo.vertexIndices = ( geoNode.PolygonVertexIndex !== undefined ) ? geoNode.PolygonVertexIndex.a : []; + + if ( geoNode.LayerElementColor ) { + + geoInfo.color = getColors( geoNode.LayerElementColor[ 0 ] ); + + } + + if ( geoNode.LayerElementMaterial ) { + + geoInfo.material = getMaterials( geoNode.LayerElementMaterial[ 0 ] ); + + } + + if ( geoNode.LayerElementNormal ) { + + geoInfo.normal = getNormals( geoNode.LayerElementNormal[ 0 ] ); + + } + + if ( geoNode.LayerElementUV ) { + + geoInfo.uv = []; + + var i = 0; + while ( geoNode.LayerElementUV[ i ] ) { + + geoInfo.uv.push( getUVs( geoNode.LayerElementUV[ i ] ) ); + i ++; + + } + + } + + geoInfo.weightTable = {}; + + if ( skeleton !== null ) { + + geoInfo.skeleton = skeleton; + + skeleton.rawBones.forEach( function ( rawBone, i ) { + + // loop over the bone's vertex indices and weights + rawBone.indices.forEach( function ( index, j ) { + + if ( geoInfo.weightTable[ index ] === undefined ) geoInfo.weightTable[ index ] = []; + + geoInfo.weightTable[ index ].push( { + + id: i, + weight: rawBone.weights[ j ], + + } ); + + } ); + + } ); + + } + + return geoInfo; + + } + + function genBuffers( geoInfo ) { + + var buffers = { + vertex: [], + normal: [], + colors: [], + uvs: [], + materialIndex: [], + vertexWeights: [], + weightsIndices: [], + }; + + var polygonIndex = 0; + var faceLength = 0; + var displayedWeightsWarning = false; + + // these will hold data for a single face + var facePositionIndexes = []; + var faceNormals = []; + var faceColors = []; + var faceUVs = []; + var faceWeights = []; + var faceWeightIndices = []; + + geoInfo.vertexIndices.forEach( function ( vertexIndex, polygonVertexIndex ) { + + var endOfFace = false; + + // Face index and vertex index arrays are combined in a single array + // A cube with quad faces looks like this: + // PolygonVertexIndex: *24 { + // a: 0, 1, 3, -3, 2, 3, 5, -5, 4, 5, 7, -7, 6, 7, 1, -1, 1, 7, 5, -4, 6, 0, 2, -5 + // } + // Negative numbers mark the end of a face - first face here is 0, 1, 3, -3 + // to find index of last vertex bit shift the index: ^ - 1 + if ( vertexIndex < 0 ) { + + vertexIndex = vertexIndex ^ - 1; // equivalent to ( x * -1 ) - 1 + endOfFace = true; + + } + + var weightIndices = []; + var weights = []; + + facePositionIndexes.push( vertexIndex * 3, vertexIndex * 3 + 1, vertexIndex * 3 + 2 ); + + if ( geoInfo.color ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.color ); + + faceColors.push( data[ 0 ], data[ 1 ], data[ 2 ] ); + + } + + if ( geoInfo.skeleton ) { + + if ( geoInfo.weightTable[ vertexIndex ] !== undefined ) { + + geoInfo.weightTable[ vertexIndex ].forEach( function ( wt ) { + + weights.push( wt.weight ); + weightIndices.push( wt.id ); + + } ); + + + } + + if ( weights.length > 4 ) { + + if ( ! displayedWeightsWarning ) { + + console.warn( 'THREE.FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights.' ); + displayedWeightsWarning = true; + + } + + var wIndex = [ 0, 0, 0, 0 ]; + var Weight = [ 0, 0, 0, 0 ]; + + weights.forEach( function ( weight, weightIndex ) { + + var currentWeight = weight; + var currentIndex = weightIndices[ weightIndex ]; + + Weight.forEach( function ( comparedWeight, comparedWeightIndex, comparedWeightArray ) { + + if ( currentWeight > comparedWeight ) { + + comparedWeightArray[ comparedWeightIndex ] = currentWeight; + currentWeight = comparedWeight; + + var tmp = wIndex[ comparedWeightIndex ]; + wIndex[ comparedWeightIndex ] = currentIndex; + currentIndex = tmp; + + } + + } ); + + } ); + + weightIndices = wIndex; + weights = Weight; + + } + + // if the weight array is shorter than 4 pad with 0s + while ( weights.length < 4 ) { + + weights.push( 0 ); + weightIndices.push( 0 ); + + } + + for ( var i = 0; i < 4; ++ i ) { + + faceWeights.push( weights[ i ] ); + faceWeightIndices.push( weightIndices[ i ] ); + + } + + } + + if ( geoInfo.normal ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.normal ); + + faceNormals.push( data[ 0 ], data[ 1 ], data[ 2 ] ); + + } + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + var materialIndex = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.material )[ 0 ]; + + } + + if ( geoInfo.uv ) { + + geoInfo.uv.forEach( function ( uv, i ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, uv ); + + if ( faceUVs[ i ] === undefined ) { + + faceUVs[ i ] = []; + + } + + faceUVs[ i ].push( data[ 0 ] ); + faceUVs[ i ].push( data[ 1 ] ); + + } ); + + } + + faceLength ++; + + if ( endOfFace ) { + + genFace( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ); + + polygonIndex ++; + faceLength = 0; + + // reset arrays for the next face + facePositionIndexes = []; + faceNormals = []; + faceColors = []; + faceUVs = []; + faceWeights = []; + faceWeightIndices = []; + + } + + } ); + + return buffers; + + } + + // Generate data for a single face in a geometry. If the face is a quad then split it into 2 tris + function genFace( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ) { + + for ( var i = 2; i < faceLength; i ++ ) { + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 0 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 2 ] ] ); + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 2 ] ] ); + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 2 ] ] ); + + if ( geoInfo.skeleton ) { + + buffers.vertexWeights.push( faceWeights[ 0 ] ); + buffers.vertexWeights.push( faceWeights[ 1 ] ); + buffers.vertexWeights.push( faceWeights[ 2 ] ); + buffers.vertexWeights.push( faceWeights[ 3 ] ); + + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 1 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 2 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 3 ] ); + + buffers.vertexWeights.push( faceWeights[ i * 4 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 1 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 2 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ 0 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ i * 4 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 3 ] ); + + } + + if ( geoInfo.color ) { + + buffers.colors.push( faceColors[ 0 ] ); + buffers.colors.push( faceColors[ 1 ] ); + buffers.colors.push( faceColors[ 2 ] ); + + buffers.colors.push( faceColors[ ( i - 1 ) * 3 ] ); + buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 1 ] ); + buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 2 ] ); + + buffers.colors.push( faceColors[ i * 3 ] ); + buffers.colors.push( faceColors[ i * 3 + 1 ] ); + buffers.colors.push( faceColors[ i * 3 + 2 ] ); + + } + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + buffers.materialIndex.push( materialIndex ); + buffers.materialIndex.push( materialIndex ); + buffers.materialIndex.push( materialIndex ); + + } + + if ( geoInfo.normal ) { + + buffers.normal.push( faceNormals[ 0 ] ); + buffers.normal.push( faceNormals[ 1 ] ); + buffers.normal.push( faceNormals[ 2 ] ); + + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 ] ); + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 1 ] ); + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 2 ] ); + + buffers.normal.push( faceNormals[ i * 3 ] ); + buffers.normal.push( faceNormals[ i * 3 + 1 ] ); + buffers.normal.push( faceNormals[ i * 3 + 2 ] ); + + } + + if ( geoInfo.uv ) { + + geoInfo.uv.forEach( function ( uv, j ) { + + if ( buffers.uvs[ j ] === undefined ) buffers.uvs[ j ] = []; + + buffers.uvs[ j ].push( faceUVs[ j ][ 0 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ 1 ] ); + + buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 + 1 ] ); + + buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 + 1 ] ); + + } ); + + } + + } + + } + + function addMorphTargets( FBXTree, parentGeo, parentGeoNode, morphTarget, preTransform ) { + + if ( morphTarget === null ) return; + + parentGeo.morphAttributes.position = []; + parentGeo.morphAttributes.normal = []; + + morphTarget.rawTargets.forEach( function ( rawTarget ) { + + var morphGeoNode = FBXTree.Objects.Geometry[ rawTarget.geoID ]; + + if ( morphGeoNode !== undefined ) { + + genMorphGeometry( parentGeo, parentGeoNode, morphGeoNode, preTransform ); + + } + + } ); + + } + + // a morph geometry node is similar to a standard node, and the node is also contained + // in FBXTree.Objects.Geometry, however it can only have attributes for position, normal + // and a special attribute Index defining which vertices of the original geometry are affected + // Normal and position attributes only have data for the vertices that are affected by the morph + function genMorphGeometry( parentGeo, parentGeoNode, morphGeoNode, preTransform ) { + + var morphGeo = new THREE.BufferGeometry(); + if ( morphGeoNode.attrName ) morphGeo.name = morphGeoNode.attrName; + + var vertexIndices = ( parentGeoNode.PolygonVertexIndex !== undefined ) ? parentGeoNode.PolygonVertexIndex.a : []; + + // make a copy of the parent's vertex positions + var vertexPositions = ( parentGeoNode.Vertices !== undefined ) ? parentGeoNode.Vertices.a.slice() : []; + + var morphPositions = ( morphGeoNode.Vertices !== undefined ) ? morphGeoNode.Vertices.a : []; + var indices = ( morphGeoNode.Indexes !== undefined ) ? morphGeoNode.Indexes.a : []; + + for ( var i = 0; i < indices.length; i ++ ) { + + var morphIndex = indices[ i ] * 3; + + // FBX format uses blend shapes rather than morph targets. This can be converted + // by additively combining the blend shape positions with the original geometry's positions + vertexPositions[ morphIndex ] += morphPositions[ i * 3 ]; + vertexPositions[ morphIndex + 1 ] += morphPositions[ i * 3 + 1 ]; + vertexPositions[ morphIndex + 2 ] += morphPositions[ i * 3 + 2 ]; + + } + + // TODO: add morph normal support + var morphGeoInfo = { + vertexIndices: vertexIndices, + vertexPositions: vertexPositions, + }; + + var morphBuffers = genBuffers( morphGeoInfo ); + + var positionAttribute = new THREE.Float32BufferAttribute( morphBuffers.vertex, 3 ); + positionAttribute.name = morphGeoNode.attrName; + + preTransform.applyToBufferAttribute( positionAttribute ); + + parentGeo.morphAttributes.position.push( positionAttribute ); + + } + + // Parse normal from FBXTree.Objects.Geometry.LayerElementNormal if it exists + function getNormals( NormalNode ) { + + var mappingType = NormalNode.MappingInformationType; + var referenceType = NormalNode.ReferenceInformationType; + var buffer = NormalNode.Normals.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + if ( 'NormalIndex' in NormalNode ) { + + indexBuffer = NormalNode.NormalIndex.a; + + } else if ( 'NormalsIndex' in NormalNode ) { + + indexBuffer = NormalNode.NormalsIndex.a; + + } + + } + + return { + dataSize: 3, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + } + + // Parse UVs from FBXTree.Objects.Geometry.LayerElementUV if it exists + function getUVs( UVNode ) { + + var mappingType = UVNode.MappingInformationType; + var referenceType = UVNode.ReferenceInformationType; + var buffer = UVNode.UV.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + indexBuffer = UVNode.UVIndex.a; + + } + + return { + dataSize: 2, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + } + + // Parse Vertex Colors from FBXTree.Objects.Geometry.LayerElementColor if it exists + function getColors( ColorNode ) { + + var mappingType = ColorNode.MappingInformationType; + var referenceType = ColorNode.ReferenceInformationType; + var buffer = ColorNode.Colors.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + indexBuffer = ColorNode.ColorIndex.a; + + } + + return { + dataSize: 4, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + } + + // Parse mapping and material data in FBXTree.Objects.Geometry.LayerElementMaterial if it exists + function getMaterials( MaterialNode ) { + + var mappingType = MaterialNode.MappingInformationType; + var referenceType = MaterialNode.ReferenceInformationType; + + if ( mappingType === 'NoMappingInformation' ) { + + return { + dataSize: 1, + buffer: [ 0 ], + indices: [ 0 ], + mappingType: 'AllSame', + referenceType: referenceType + }; + + } + + var materialIndexBuffer = MaterialNode.Materials.a; + + // Since materials are stored as indices, there's a bit of a mismatch between FBX and what + // we expect.So we create an intermediate buffer that points to the index in the buffer, + // for conforming with the other functions we've written for other data. + var materialIndices = []; + + for ( var i = 0; i < materialIndexBuffer.length; ++ i ) { + + materialIndices.push( i ); + + } + + return { + dataSize: 1, + buffer: materialIndexBuffer, + indices: materialIndices, + mappingType: mappingType, + referenceType: referenceType + }; + + } + + var dataArray = []; + + function getData( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) { + + var index; + + switch ( infoObject.mappingType ) { + + case 'ByPolygonVertex' : + index = polygonVertexIndex; + break; + case 'ByPolygon' : + index = polygonIndex; + break; + case 'ByVertice' : + index = vertexIndex; + break; + case 'AllSame' : + index = infoObject.indices[ 0 ]; + break; + default : + console.warn( 'THREE.FBXLoader: unknown attribute mapping type ' + infoObject.mappingType ); + + } + + if ( infoObject.referenceType === 'IndexToDirect' ) index = infoObject.indices[ index ]; + + var from = index * infoObject.dataSize; + var to = from + infoObject.dataSize; + + return slice( dataArray, infoObject.buffer, from, to ); + + } + + // Generate a NurbGeometry from a node in FBXTree.Objects.Geometry + function parseNurbsGeometry( geoNode ) { + + if ( THREE.NURBSCurve === undefined ) { + + console.error( 'THREE.FBXLoader: The loader relies on THREE.NURBSCurve for any nurbs present in the model. Nurbs will show up as empty geometry.' ); + return new THREE.BufferGeometry(); + + } + + var order = parseInt( geoNode.Order ); + + if ( isNaN( order ) ) { + + console.error( 'THREE.FBXLoader: Invalid Order %s given for geometry ID: %s', geoNode.Order, geoNode.id ); + return new THREE.BufferGeometry(); + + } + + var degree = order - 1; + + var knots = geoNode.KnotVector.a; + var controlPoints = []; + var pointsValues = geoNode.Points.a; + + for ( var i = 0, l = pointsValues.length; i < l; i += 4 ) { + + controlPoints.push( new THREE.Vector4().fromArray( pointsValues, i ) ); + + } + + var startKnot, endKnot; + + if ( geoNode.Form === 'Closed' ) { + + controlPoints.push( controlPoints[ 0 ] ); + + } else if ( geoNode.Form === 'Periodic' ) { + + startKnot = degree; + endKnot = knots.length - 1 - startKnot; + + for ( var i = 0; i < degree; ++ i ) { + + controlPoints.push( controlPoints[ i ] ); + + } + + } + + var curve = new THREE.NURBSCurve( degree, knots, controlPoints, startKnot, endKnot ); + var vertices = curve.getPoints( controlPoints.length * 7 ); + + var positions = new Float32Array( vertices.length * 3 ); + + vertices.forEach( function ( vertex, i ) { + + vertex.toArray( positions, i * 3 ); + + } ); + + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + + return geometry; + + } + + // create the main THREE.Group() to be returned by the loader + function parseScene( FBXTree, connections, skeletons, geometryMap, materialMap ) { + + var sceneGraph = new THREE.Group(); + + var modelMap = parseModels( FBXTree, skeletons, geometryMap, materialMap, connections ); + + var modelNodes = FBXTree.Objects.Model; + + modelMap.forEach( function ( model ) { + + var modelNode = modelNodes[ model.ID ]; + setLookAtProperties( FBXTree, model, modelNode, connections, sceneGraph ); + + var parentConnections = connections.get( model.ID ).parents; + + parentConnections.forEach( function ( connection ) { + + var parent = modelMap.get( connection.ID ); + if ( parent !== undefined ) parent.add( model ); + + } ); + + if ( model.parent === null ) { + + sceneGraph.add( model ); + + } + + + } ); + + bindSkeleton( FBXTree, skeletons, geometryMap, modelMap, connections ); + + addAnimations( FBXTree, connections, sceneGraph ); + + createAmbientLight( FBXTree, sceneGraph ); + + return sceneGraph; + + } + + // parse nodes in FBXTree.Objects.Model + function parseModels( FBXTree, skeletons, geometryMap, materialMap, connections ) { + + var modelMap = new Map(); + var modelNodes = FBXTree.Objects.Model; + + for ( var nodeID in modelNodes ) { + + var id = parseInt( nodeID ); + var node = modelNodes[ nodeID ]; + var relationships = connections.get( id ); + + var model = buildSkeleton( relationships, skeletons, id, node.attrName ); + + if ( ! model ) { + + switch ( node.attrType ) { + + case 'Camera': + model = createCamera( FBXTree, relationships ); + break; + case 'Light': + model = createLight( FBXTree, relationships ); + break; + case 'Mesh': + model = createMesh( FBXTree, relationships, geometryMap, materialMap ); + break; + case 'NurbsCurve': + model = createCurve( relationships, geometryMap ); + break; + case 'LimbNode': // usually associated with a Bone, however if a Bone was not created we'll make a Group instead + case 'Null': + default: + model = new THREE.Group(); + break; + + } + + model.name = THREE.PropertyBinding.sanitizeNodeName( node.attrName ); + model.ID = id; + + } + + setModelTransforms( FBXTree, model, node ); + modelMap.set( id, model ); + + } + + return modelMap; + + } + + function buildSkeleton( relationships, skeletons, id, name ) { + + var bone = null; + + relationships.parents.forEach( function ( parent ) { + + for ( var ID in skeletons ) { + + var skeleton = skeletons[ ID ]; + + skeleton.rawBones.forEach( function ( rawBone, i ) { + + if ( rawBone.ID === parent.ID ) { + + var subBone = bone; + bone = new THREE.Bone(); + bone.matrixWorld.copy( rawBone.transformLink ); + + // set name and id here - otherwise in cases where "subBone" is created it will not have a name / id + bone.name = THREE.PropertyBinding.sanitizeNodeName( name ); + bone.ID = id; + + skeleton.bones[ i ] = bone; + + // In cases where a bone is shared between multiple meshes + // duplicate the bone here and and it as a child of the first bone + if ( subBone !== null ) { + + bone.add( subBone ); + + } + + } + + } ); + + } + + } ); + + return bone; + + } + + // create a THREE.PerspectiveCamera or THREE.OrthographicCamera + function createCamera( FBXTree, relationships ) { + + var model; + var cameraAttribute; + + relationships.children.forEach( function ( child ) { + + var attr = FBXTree.Objects.NodeAttribute[ child.ID ]; + + if ( attr !== undefined ) { + + cameraAttribute = attr; + + } + + } ); + + if ( cameraAttribute === undefined ) { + + model = new THREE.Object3D(); + + } else { + + var type = 0; + if ( cameraAttribute.CameraProjectionType !== undefined && cameraAttribute.CameraProjectionType.value === 1 ) { + + type = 1; + + } + + var nearClippingPlane = 1; + if ( cameraAttribute.NearPlane !== undefined ) { + + nearClippingPlane = cameraAttribute.NearPlane.value / 1000; + + } + + var farClippingPlane = 1000; + if ( cameraAttribute.FarPlane !== undefined ) { + + farClippingPlane = cameraAttribute.FarPlane.value / 1000; + + } + + + var width = window.innerWidth; + var height = window.innerHeight; + + if ( cameraAttribute.AspectWidth !== undefined && cameraAttribute.AspectHeight !== undefined ) { + + width = cameraAttribute.AspectWidth.value; + height = cameraAttribute.AspectHeight.value; + + } + + var aspect = width / height; + + var fov = 45; + if ( cameraAttribute.FieldOfView !== undefined ) { + + fov = cameraAttribute.FieldOfView.value; + + } + + var focalLength = cameraAttribute.FocalLength ? cameraAttribute.FocalLength.value : null; + + switch ( type ) { + + case 0: // Perspective + model = new THREE.PerspectiveCamera( fov, aspect, nearClippingPlane, farClippingPlane ); + if ( focalLength !== null ) model.setFocalLength( focalLength ); + break; + + case 1: // Orthographic + model = new THREE.OrthographicCamera( - width / 2, width / 2, height / 2, - height / 2, nearClippingPlane, farClippingPlane ); + break; + + default: + console.warn( 'THREE.FBXLoader: Unknown camera type ' + type + '.' ); + model = new THREE.Object3D(); + break; + + } + + } + + return model; + + } + + // Create a THREE.DirectionalLight, THREE.PointLight or THREE.SpotLight + function createLight( FBXTree, relationships ) { + + var model; + var lightAttribute; + + relationships.children.forEach( function ( child ) { + + var attr = FBXTree.Objects.NodeAttribute[ child.ID ]; + + if ( attr !== undefined ) { + + lightAttribute = attr; + + } + + } ); + + if ( lightAttribute === undefined ) { + + model = new THREE.Object3D(); + + } else { + + var type; + + // LightType can be undefined for Point lights + if ( lightAttribute.LightType === undefined ) { + + type = 0; + + } else { + + type = lightAttribute.LightType.value; + + } + + var color = 0xffffff; + + if ( lightAttribute.Color !== undefined ) { + + color = new THREE.Color().fromArray( lightAttribute.Color.value ); + + } + + var intensity = ( lightAttribute.Intensity === undefined ) ? 1 : lightAttribute.Intensity.value / 100; + + // light disabled + if ( lightAttribute.CastLightOnObject !== undefined && lightAttribute.CastLightOnObject.value === 0 ) { + + intensity = 0; + + } + + var distance = 0; + if ( lightAttribute.FarAttenuationEnd !== undefined ) { + + if ( lightAttribute.EnableFarAttenuation !== undefined && lightAttribute.EnableFarAttenuation.value === 0 ) { + + distance = 0; + + } else { + + distance = lightAttribute.FarAttenuationEnd.value; + + } + + } + + // TODO: could this be calculated linearly from FarAttenuationStart to FarAttenuationEnd? + var decay = 1; + + switch ( type ) { + + case 0: // Point + model = new THREE.PointLight( color, intensity, distance, decay ); + break; + + case 1: // Directional + model = new THREE.DirectionalLight( color, intensity ); + break; + + case 2: // Spot + var angle = Math.PI / 3; + + if ( lightAttribute.InnerAngle !== undefined ) { + + angle = THREE.Math.degToRad( lightAttribute.InnerAngle.value ); + + } + + var penumbra = 0; + if ( lightAttribute.OuterAngle !== undefined ) { + + // TODO: this is not correct - FBX calculates outer and inner angle in degrees + // with OuterAngle > InnerAngle && OuterAngle <= Math.PI + // while three.js uses a penumbra between (0, 1) to attenuate the inner angle + penumbra = THREE.Math.degToRad( lightAttribute.OuterAngle.value ); + penumbra = Math.max( penumbra, 1 ); + + } + + model = new THREE.SpotLight( color, intensity, distance, angle, penumbra, decay ); + break; + + default: + console.warn( 'THREE.FBXLoader: Unknown light type ' + lightAttribute.LightType.value + ', defaulting to a THREE.PointLight.' ); + model = new THREE.PointLight( color, intensity ); + break; + + } + + if ( lightAttribute.CastShadows !== undefined && lightAttribute.CastShadows.value === 1 ) { + + model.castShadow = true; + + } + + } + + return model; + + } + + function createMesh( FBXTree, relationships, geometryMap, materialMap ) { + + var model; + var geometry = null; + var material = null; + var materials = []; + + // get geometry and materials(s) from connections + relationships.children.forEach( function ( child ) { + + if ( geometryMap.has( child.ID ) ) { + + geometry = geometryMap.get( child.ID ); + + } + + if ( materialMap.has( child.ID ) ) { + + materials.push( materialMap.get( child.ID ) ); + + } + + } ); + + if ( materials.length > 1 ) { + + material = materials; + + } else if ( materials.length > 0 ) { + + material = materials[ 0 ]; + + } else { + + material = new THREE.MeshPhongMaterial( { color: 0xcccccc } ); + materials.push( material ); + + } + + if ( 'color' in geometry.attributes ) { + + materials.forEach( function ( material ) { + + material.vertexColors = THREE.VertexColors; + + } ); + + } + + if ( geometry.FBX_Deformer ) { + + materials.forEach( function ( material ) { + + material.skinning = true; + + } ); + + model = new THREE.SkinnedMesh( geometry, material ); + + } else { + + model = new THREE.Mesh( geometry, material ); + + } + + return model; + + } + + function createCurve( relationships, geometryMap ) { + + var geometry = relationships.children.reduce( function ( geo, child ) { + + if ( geometryMap.has( child.ID ) ) geo = geometryMap.get( child.ID ); + + return geo; + + }, null ); + + // FBX does not list materials for Nurbs lines, so we'll just put our own in here. + var material = new THREE.LineBasicMaterial( { color: 0x3300ff, linewidth: 1 } ); + return new THREE.Line( geometry, material ); + + } + + // Parse ambient color in FBXTree.GlobalSettings - if it's not set to black (default), create an ambient light + function createAmbientLight( FBXTree, sceneGraph ) { + + if ( 'GlobalSettings' in FBXTree && 'AmbientColor' in FBXTree.GlobalSettings ) { + + var ambientColor = FBXTree.GlobalSettings.AmbientColor.value; + var r = ambientColor[ 0 ]; + var g = ambientColor[ 1 ]; + var b = ambientColor[ 2 ]; + + if ( r !== 0 || g !== 0 || b !== 0 ) { + + var color = new THREE.Color( r, g, b ); + sceneGraph.add( new THREE.AmbientLight( color, 1 ) ); + + } + + } + + } + + function setLookAtProperties( FBXTree, model, modelNode, connections, sceneGraph ) { + + if ( 'LookAtProperty' in modelNode ) { + + var children = connections.get( model.ID ).children; + + children.forEach( function ( child ) { + + if ( child.relationship === 'LookAtProperty' ) { + + var lookAtTarget = FBXTree.Objects.Model[ child.ID ]; + + if ( 'Lcl_Translation' in lookAtTarget ) { + + var pos = lookAtTarget.Lcl_Translation.value; + + // DirectionalLight, SpotLight + if ( model.target !== undefined ) { + + model.target.position.fromArray( pos ); + sceneGraph.add( model.target ); + + } else { // Cameras and other Object3Ds + + model.lookAt( new THREE.Vector3().fromArray( pos ) ); + + } + + } + + } + + } ); + + } + + } + + function setModelTransformsB( FBXTree, model, modelNode){ + + var transformData = {}; + + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = parseInt( modelNode.RotationOrder.value ); + if ( 'Lcl_Translation' in modelNode ) transformData.translation = modelNode.Lcl_Translation.value; + if ( 'RotationOffset' in modelNode ) transformData.rotationOffset = modelNode.RotationOffset.value; + if ( 'Lcl_Rotation' in modelNode ) transformData.rotation = modelNode.Lcl_Rotation.value; + if ( 'PreRotation' in modelNode ) transformData.preRotation = modelNode.PreRotation.value; + if ( 'PostRotation' in modelNode ) transformData.postRotation = modelNode.PostRotation.value; + if ( 'Lcl_Scaling' in modelNode ) transformData.scale = modelNode.Lcl_Scaling.value; + + var transform = generateTransform( transformData ); + + model.applyMatrix( transform ); + } + + var tempMat = new THREE.Matrix4(); + var tempEuler = new THREE.Euler(); + var tempVec = new THREE.Vector3(); + var translation = new THREE.Vector3(); + var rotation = new THREE.Matrix4(); + + // generate transformation from FBX transform data + // ref: https://help.autodesk.com/view/FBX/2017/ENU/?guid=__files_GUID_10CDD63C_79C1_4F2D_BB28_AD2BE65A02ED_htm + // transformData = { + // eulerOrder: int, + // translation: [], + // rotationOffset: [], + // preRotation + // rotation + // postRotation + // scale + // } + // all entries are optional + function generateTransform( transformData ) { + + var transform = new THREE.Matrix4(); + translation.set( 0, 0, 0 ); + rotation.identity(); + + var order = ( transformData.eulerOrder ) ? getEulerOrder( transformData.eulerOrder ) : getEulerOrder( 0 ); + + if ( transformData.translation ) translation.fromArray( transformData.translation ); + if ( transformData.rotationOffset ) translation.add( tempVec.fromArray( transformData.rotationOffset ) ); + + if ( transformData.rotation ) { + + var array = transformData.rotation.map( THREE.Math.degToRad ); + array.push( order ); + rotation.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + } + + if ( transformData.preRotation ) { + + var array = transformData.preRotation.map( THREE.Math.degToRad ); + array.push( order ); + tempMat.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + rotation.premultiply( tempMat ); + + } + + if ( transformData.postRotation ) { + + var array = transformData.postRotation.map( THREE.Math.degToRad ); + array.push( order ); + tempMat.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + tempMat.getInverse( tempMat ); + + rotation.multiply( tempMat ); + + } + + if ( transformData.scale ) transform.scale( tempVec.fromArray( transformData.scale ) ); + + transform.setPosition( translation ); + transform.multiply( rotation ); + + return transform; + + } + + // Returns the three.js intrinsic Euler order corresponding to FBX extrinsic Euler order + // ref: http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_euler_html + function getEulerOrder( order ) { + + var enums = [ + 'ZYX', // -> XYZ extrinsic + 'YZX', // -> XZY extrinsic + 'XZY', // -> YZX extrinsic + 'ZXY', // -> YXZ extrinsic + 'YXZ', // -> ZXY extrinsic + 'XYZ', // -> ZYX extrinsic + //'SphericXYZ', // not possible to support + ]; + + if ( order === 6 ) { + + console.warn( 'THREE.FBXLoader: unsupported Euler Order: Spherical XYZ. Animations and rotations may be incorrect.' ); + return enums[ 0 ]; + + } + + return enums[ order ]; + + } + + // parse the model node for transform details and apply them to the model + function setModelTransforms( FBXTree, model, modelNode ) { + + // http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_euler_html + if ( 'RotationOrder' in modelNode ) { + + var enums = [ + 'XYZ', // default + 'XZY', + 'YZX', + 'ZXY', + 'YXZ', + 'ZYX', + 'SphericXYZ', + ]; + + var value = parseInt( modelNode.RotationOrder.value, 10 ); + + if ( value > 0 && value < 6 ) { + + // model.rotation.order = enums[ value ]; + + // Note: Euler order other than XYZ is currently not supported, so just display a warning for now + //console.warn( 'THREE.FBXLoader: unsupported Euler Order: %s. Currently only XYZ order is supported. //Animations and rotations may be incorrect.', enums[ value ] ); + + } else if ( value === 6 ) { + + console.warn( 'THREE.FBXLoader: unsupported Euler Order: Spherical XYZ. Animations and rotations may be incorrect.' ); + + } + + } + + if ( 'Lcl_Translation' in modelNode ) { + + model.position.fromArray( modelNode.Lcl_Translation.value ); + + } + + if ( 'Lcl_Rotation' in modelNode ) { + var array = modelNode.Lcl_Rotation.value.map( THREE.Math.degToRad ); + if (enums===undefined){ + array.push('XZY'); + }else{ + array.push( enums[value] ); + } + model.quaternion.setFromEuler( new THREE.Euler().fromArray( array ) ); + //rotation.makeRotationFromEuler( tempEuler.fromArray( array ) ); + /*var rotation = modelNode.Lcl_Rotation.value.map( THREE.Math.degToRad ); + rotation.push( 'XZY' ); + model.quaternion.setFromEuler( new THREE.Euler().fromArray( rotation ) );*/ + + } + + if ( 'Lcl_Scaling' in modelNode ) { + + model.scale.fromArray( modelNode.Lcl_Scaling.value ); + + } + + if ( 'PreRotation' in modelNode ) { + + var array = modelNode.PreRotation.value.map( THREE.Math.degToRad ); + array[ 3 ] = 'ZYX'; + + var preRotations = new THREE.Euler().fromArray( array ); + + preRotations = new THREE.Quaternion().setFromEuler( preRotations ); + model.quaternion.premultiply( preRotations ); + + } + + } + + function bindSkeleton( FBXTree, skeletons, geometryMap, modelMap, connections ) { + + if (isEmpty(skeletons)) return; + + var bindMatrices = parsePoseNodes( FBXTree ); + + for ( var ID in skeletons ) { + + var skeleton = skeletons[ ID ]; + + var parents = connections.get( parseInt( skeleton.ID ) ).parents; + + parents.forEach( function ( parent ) { + + if ( geometryMap.has( parent.ID ) ) { + + var geoID = parent.ID; + var geoRelationships = connections.get( geoID ); + + geoRelationships.parents.forEach( function ( geoConnParent ) { + + if ( modelMap.has( geoConnParent.ID ) ) { + + var model = modelMap.get( geoConnParent.ID ); + + model.bind( new THREE.Skeleton( skeleton.bones ), bindMatrices[ geoConnParent.ID ] ); + + } + + } ); + + } + + } ); + + } + + } + + function isEmpty(map) { + for(var key in map) { + if (map.hasOwnProperty(key)) { + return false; + } + } + return true; + } + + function parsePoseNodes( FBXTree ) { + + var bindMatrices = {}; + + if ( 'Pose' in FBXTree.Objects ) { + + var BindPoseNode = FBXTree.Objects.Pose; + + for ( var nodeID in BindPoseNode ) { + + if ( BindPoseNode[ nodeID ].attrType === 'BindPose' ) { + + var poseNodes = BindPoseNode[ nodeID ].PoseNode; + + if ( Array.isArray( poseNodes ) ) { + + poseNodes.forEach( function ( poseNode ) { + + bindMatrices[ poseNode.Node ] = new THREE.Matrix4().fromArray( poseNode.Matrix.a ); + + } ); + + } else { + + bindMatrices[ poseNodes.Node ] = new THREE.Matrix4().fromArray( poseNodes.Matrix.a ); + + } + + } + + } + + } + + return bindMatrices; + + } + + function parseAnimations( FBXTree, connections ) { + + // since the actual transformation data is stored in FBXTree.Objects.AnimationCurve, + // if this is undefined we can safely assume there are no animations + if ( FBXTree.Objects.AnimationCurve === undefined ) return undefined; + + var curveNodesMap = parseAnimationCurveNodes( FBXTree ); + + parseAnimationCurves( FBXTree, connections, curveNodesMap ); + + var layersMap = parseAnimationLayers( FBXTree, connections, curveNodesMap ); + var rawClips = parseAnimStacks( FBXTree, connections, layersMap ); + + return rawClips; + + } + + // parse nodes in FBXTree.Objects.AnimationCurveNode + // each AnimationCurveNode holds data for an animation transform for a model (e.g. left arm rotation ) + // and is referenced by an AnimationLayer + function parseAnimationCurveNodes( FBXTree ) { + + var rawCurveNodes = FBXTree.Objects.AnimationCurveNode; + + var curveNodesMap = new Map(); + + for ( var nodeID in rawCurveNodes ) { + + var rawCurveNode = rawCurveNodes[ nodeID ]; + + if ( rawCurveNode.attrName.match( /S|R|T/ ) !== null ) { + + var curveNode = { + + id: rawCurveNode.id, + attr: rawCurveNode.attrName, + curves: {}, + + }; + + curveNodesMap.set( curveNode.id, curveNode ); + + } + + } + + return curveNodesMap; + + } + + // parse nodes in FBXTree.Objects.AnimationCurve and connect them up to + // previously parsed AnimationCurveNodes. Each AnimationCurve holds data for a single animated + // axis ( e.g. times and values of x rotation) + function parseAnimationCurves( FBXTree, connections, curveNodesMap ) { + + var rawCurves = FBXTree.Objects.AnimationCurve; + + for ( var nodeID in rawCurves ) { + + var animationCurve = { + + id: rawCurves[ nodeID ].id, + times: rawCurves[ nodeID ].KeyTime.a.map( convertFBXTimeToSeconds ), + values: rawCurves[ nodeID ].KeyValueFloat.a, + + }; + + var relationships = connections.get( animationCurve.id ); + + if ( relationships !== undefined ) { + + var animationCurveID = relationships.parents[ 0 ].ID; + var animationCurveRelationship = relationships.parents[ 0 ].relationship; + + if ( animationCurveRelationship.match( /X/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'x' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /Y/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'y' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /Z/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'z' ] = animationCurve; + + } + + } + + } + + } + + // parse nodes in FBXTree.Objects.AnimationLayer. Each layers holds references + // to various AnimationCurveNodes and is referenced by an AnimationStack node + // note: theoretically a stack can have multiple layers, however in practice there always seems to be one per stack + function parseAnimationLayers( FBXTree, connections, curveNodesMap ) { + + var rawLayers = FBXTree.Objects.AnimationLayer; + + var layersMap = new Map(); + + for ( var nodeID in rawLayers ) { + + var layerCurveNodes = []; + + var connection = connections.get( parseInt( nodeID ) ); + + if ( connection !== undefined ) { + + // all the animationCurveNodes used in the layer + var children = connection.children; + + children.forEach( function ( child, i ) { + + if ( curveNodesMap.has( child.ID ) ) { + + var curveNode = curveNodesMap.get( child.ID ); + + // check that the curves are defined for at least one axis, otherwise ignore the curveNode + if ( curveNode.curves.x !== undefined || curveNode.curves.y !== undefined || curveNode.curves.z !== undefined ) { + + if ( layerCurveNodes[ i ] === undefined ) { + + var modelID; + + connections.get( child.ID ).parents.forEach( function ( parent ) { + + if ( parent.relationship !== undefined ) modelID = parent.ID; + + } ); + + var rawModel = FBXTree.Objects.Model[ modelID.toString() ]; + + var node = { + + modelName: THREE.PropertyBinding.sanitizeNodeName( rawModel.attrName ), + initialPosition: [ 0, 0, 0 ], + initialRotation: [ 0, 0, 0 ], + initialScale: [ 1, 1, 1 ], + + }; + + if ( 'Lcl_Translation' in rawModel ) node.initialPosition = rawModel.Lcl_Translation.value; + + if ( 'Lcl_Rotation' in rawModel ) node.initialRotation = rawModel.Lcl_Rotation.value; + + if ( 'Lcl_Scaling' in rawModel ) node.initialScale = rawModel.Lcl_Scaling.value; + + // if the animated model is pre rotated, we'll have to apply the pre rotations to every + // animation value as well + if ( 'PreRotation' in rawModel ) node.preRotations = rawModel.PreRotation.value; + + layerCurveNodes[ i ] = node; + + } + + layerCurveNodes[ i ][ curveNode.attr ] = curveNode; + + } + + + + } + + } ); + + layersMap.set( parseInt( nodeID ), layerCurveNodes ); + + } + + } + + return layersMap; + + } + + // parse nodes in FBXTree.Objects.AnimationStack. These are the top level node in the animation + // hierarchy. Each Stack node will be used to create a THREE.AnimationClip + function parseAnimStacks( FBXTree, connections, layersMap ) { + + var rawStacks = FBXTree.Objects.AnimationStack; + + // connect the stacks (clips) up to the layers + var rawClips = {}; + + for ( var nodeID in rawStacks ) { + + var children = connections.get( parseInt( nodeID ) ).children; + + if ( children.length > 1 ) { + + // it seems like stacks will always be associated with a single layer. But just in case there are files + // where there are multiple layers per stack, we'll display a warning + console.warn( 'THREE.FBXLoader: Encountered an animation stack with multiple layers, this is currently not supported. Ignoring subsequent layers.' ); + + } + + var layer = layersMap.get( children[ 0 ].ID ); + + rawClips[ nodeID ] = { + + name: rawStacks[ nodeID ].attrName, + layer: layer, + + }; + + } + + return rawClips; + + } + + // take raw animation data from parseAnimations and connect it up to the loaded models + function addAnimations( FBXTree, connections, sceneGraph ) { + + sceneGraph.animations = []; + + var rawClips = parseAnimations( FBXTree, connections ); + + if ( rawClips === undefined ) return; + + + for ( var key in rawClips ) { + + var rawClip = rawClips[ key ]; + + var clip = addClip( rawClip ); + + sceneGraph.animations.push( clip ); + + } + + } + + function addClip( rawClip ) { + + var tracks = []; + + rawClip.layer.forEach( function ( rawTracks ) { + + tracks = tracks.concat( generateTracks( rawTracks ) ); + + } ); + + return new THREE.AnimationClip( rawClip.name, - 1, tracks ); + + } + + function generateTracks( rawTracks ) { + + var tracks = []; + + if ( rawTracks.T !== undefined && Object.keys( rawTracks.T.curves ).length > 0 ) { + + var positionTrack = generateVectorTrack( rawTracks.modelName, rawTracks.T.curves, rawTracks.initialPosition, 'position' ); + if ( positionTrack !== undefined ) tracks.push( positionTrack ); + + } + + if ( rawTracks.R !== undefined && Object.keys( rawTracks.R.curves ).length > 0 ) { + + var rotationTrack = generateRotationTrack( rawTracks.modelName, rawTracks.R.curves, rawTracks.initialRotation, rawTracks.preRotations ); + if ( rotationTrack !== undefined ) tracks.push( rotationTrack ); + + } + + if ( rawTracks.S !== undefined && Object.keys( rawTracks.S.curves ).length > 0 ) { + + var scaleTrack = generateVectorTrack( rawTracks.modelName, rawTracks.S.curves, rawTracks.initialScale, 'scale' ); + if ( scaleTrack !== undefined ) tracks.push( scaleTrack ); + + } + + return tracks; + + } + + function generateVectorTrack( modelName, curves, initialValue, type ) { + + var times = getTimesForAllAxes( curves ); + var values = getKeyframeTrackValues( times, curves, initialValue ); + + return new THREE.VectorKeyframeTrack( modelName + '.' + type, times, values ); + + } + + function generateRotationTrack( modelName, curves, initialValue, preRotations ) { + + if ( curves.x !== undefined ) { + + interpolateRotations( curves.x ); + curves.x.values = curves.x.values.map( THREE.Math.degToRad ); + + } + if ( curves.y !== undefined ) { + + interpolateRotations( curves.y ); + curves.y.values = curves.y.values.map( THREE.Math.degToRad ); + + } + if ( curves.z !== undefined ) { + + interpolateRotations( curves.z ); + curves.z.values = curves.z.values.map( THREE.Math.degToRad ); + + } + + var times = getTimesForAllAxes( curves ); + var values = getKeyframeTrackValues( times, curves, initialValue ); + + if ( preRotations !== undefined ) { + + preRotations = preRotations.map( THREE.Math.degToRad ); + preRotations.push( 'ZYX' ); + + preRotations = new THREE.Euler().fromArray( preRotations ); + preRotations = new THREE.Quaternion().setFromEuler( preRotations ); + + } + + var quaternion = new THREE.Quaternion(); + var euler = new THREE.Euler(); + + var quaternionValues = []; + + for ( var i = 0; i < values.length; i += 3 ) { + + euler.set( values[ i ], values[ i + 1 ], values[ i + 2 ], 'ZYX' ); + + quaternion.setFromEuler( euler ); + + if ( preRotations !== undefined )quaternion.premultiply( preRotations ); + + quaternion.toArray( quaternionValues, ( i / 3 ) * 4 ); + + } + + return new THREE.QuaternionKeyframeTrack( modelName + '.quaternion', times, quaternionValues ); + + } + + function getKeyframeTrackValues( times, curves, initialValue ) { + + var prevValue = initialValue; + + var values = []; + + var xIndex = - 1; + var yIndex = - 1; + var zIndex = - 1; + + times.forEach( function ( time ) { + + if ( curves.x ) xIndex = curves.x.times.indexOf( time ); + if ( curves.y ) yIndex = curves.y.times.indexOf( time ); + if ( curves.z ) zIndex = curves.z.times.indexOf( time ); + + // if there is an x value defined for this frame, use that + if ( xIndex !== - 1 ) { + + var xValue = curves.x.values[ xIndex ]; + values.push( xValue ); + prevValue[ 0 ] = xValue; + + } else { + + // otherwise use the x value from the previous frame + values.push( prevValue[ 0 ] ); + + } + + if ( yIndex !== - 1 ) { + + var yValue = curves.y.values[ yIndex ]; + values.push( yValue ); + prevValue[ 1 ] = yValue; + + } else { + + values.push( prevValue[ 1 ] ); + + } + + if ( zIndex !== - 1 ) { + + var zValue = curves.z.values[ zIndex ]; + values.push( zValue ); + prevValue[ 2 ] = zValue; + + } else { + + values.push( prevValue[ 2 ] ); + + } + + } ); + + return values; + + } + + // For all animated objects, times are defined separately for each axis + // Here we'll combine the times into one sorted array without duplicates + function getTimesForAllAxes( curves ) { + + var times = []; + + // first join together the times for each axis, if defined + if ( curves.x !== undefined ) times = times.concat( curves.x.times ); + if ( curves.y !== undefined ) times = times.concat( curves.y.times ); + if ( curves.z !== undefined ) times = times.concat( curves.z.times ); + + // then sort them and remove duplicates + times = times.sort( function ( a, b ) { + + return a - b; + + } ).filter( function ( elem, index, array ) { + + return array.indexOf( elem ) == index; + + } ); + + return times; + + } + + // Rotations are defined as Euler angles which can have values of any size + // These will be converted to quaternions which don't support values greater than + // PI, so we'll interpolate large rotations + function interpolateRotations( curve ) { + + for ( var i = 1; i < curve.values.length; i ++ ) { + + var initialValue = curve.values[ i - 1 ]; + var valuesSpan = curve.values[ i ] - initialValue; + + var absoluteSpan = Math.abs( valuesSpan ); + + if ( absoluteSpan >= 180 ) { + + var numSubIntervals = absoluteSpan / 180; + + var step = valuesSpan / numSubIntervals; + var nextValue = initialValue + step; + + var initialTime = curve.times[ i - 1 ]; + var timeSpan = curve.times[ i ] - initialTime; + var interval = timeSpan / numSubIntervals; + var nextTime = initialTime + interval; + + var interpolatedTimes = []; + var interpolatedValues = []; + + while ( nextTime < curve.times[ i ] ) { + + interpolatedTimes.push( nextTime ); + nextTime += interval; + + interpolatedValues.push( nextValue ); + nextValue += step; + + } + + curve.times = inject( curve.times, i, interpolatedTimes ); + curve.values = inject( curve.values, i, interpolatedValues ); + + } + + } + + } + + // parse an FBX file in ASCII format + function TextParser() {} + + Object.assign( TextParser.prototype, { + + getPrevNode: function () { + + return this.nodeStack[ this.currentIndent - 2 ]; + + }, + + getCurrentNode: function () { + + return this.nodeStack[ this.currentIndent - 1 ]; + + }, + + getCurrentProp: function () { + + return this.currentProp; + + }, + + pushStack: function ( node ) { + + this.nodeStack.push( node ); + this.currentIndent += 1; + + }, + + popStack: function () { + + this.nodeStack.pop(); + this.currentIndent -= 1; + + }, + + setCurrentProp: function ( val, name ) { + + this.currentProp = val; + this.currentPropName = name; + + }, + + parse: function ( text ) { + + this.currentIndent = 0; + this.allNodes = new FBXTree(); + this.nodeStack = []; + this.currentProp = []; + this.currentPropName = ''; + + var self = this; + + var split = text.split( '\n' ); + + split.forEach( function ( line, i ) { + + var matchComment = line.match( /^[\s\t]*;/ ); + var matchEmpty = line.match( /^[\s\t]*$/ ); + + if ( matchComment || matchEmpty ) return; + + var matchBeginning = line.match( '^\\t{' + self.currentIndent + '}(\\w+):(.*){', '' ); + var matchProperty = line.match( '^\\t{' + ( self.currentIndent ) + '}(\\w+):[\\s\\t\\r\\n](.*)' ); + var matchEnd = line.match( '^\\t{' + ( self.currentIndent - 1 ) + '}}' ); + + if ( matchBeginning ) { + + self.parseNodeBegin( line, matchBeginning ); + + } else if ( matchProperty ) { + + self.parseNodeProperty( line, matchProperty, split[ ++ i ] ); + + } else if ( matchEnd ) { + + self.popStack(); + + } else if ( line.match( /^[^\s\t}]/ ) ) { + + // large arrays are split over multiple lines terminated with a ',' character + // if this is encountered the line needs to be joined to the previous line + self.parseNodePropertyContinued( line ); + + } + + } ); + + return this.allNodes; + + }, + + parseNodeBegin: function ( line, property ) { + + var nodeName = property[ 1 ].trim().replace( /^"/, '' ).replace( /"$/, '' ); + + var nodeAttrs = property[ 2 ].split( ',' ).map( function ( attr ) { + + return attr.trim().replace( /^"/, '' ).replace( /"$/, '' ); + + } ); + + var node = { name: nodeName }; + var attrs = this.parseNodeAttr( nodeAttrs ); + + var currentNode = this.getCurrentNode(); + + // a top node + if ( this.currentIndent === 0 ) { + + this.allNodes.add( nodeName, node ); + + } else { // a subnode + + // if the subnode already exists, append it + if ( nodeName in currentNode ) { + + // special case Pose needs PoseNodes as an array + if ( nodeName === 'PoseNode' ) { + + currentNode.PoseNode.push( node ); + + } else if ( currentNode[ nodeName ].id !== undefined ) { + + currentNode[ nodeName ] = {}; + currentNode[ nodeName ][ currentNode[ nodeName ].id ] = currentNode[ nodeName ]; + + } + + if ( attrs.id !== '' ) currentNode[ nodeName ][ attrs.id ] = node; + + } else if ( typeof attrs.id === 'number' ) { + + currentNode[ nodeName ] = {}; + currentNode[ nodeName ][ attrs.id ] = node; + + } else if ( nodeName !== 'Properties70' ) { + + if ( nodeName === 'PoseNode' ) currentNode[ nodeName ] = [ node ]; + else currentNode[ nodeName ] = node; + + } + + } + + if ( typeof attrs.id === 'number' ) node.id = attrs.id; + if ( attrs.name !== '' ) node.attrName = attrs.name; + if ( attrs.type !== '' ) node.attrType = attrs.type; + + this.pushStack( node ); + + }, + + parseNodeAttr: function ( attrs ) { + + var id = attrs[ 0 ]; + + if ( attrs[ 0 ] !== '' ) { + + id = parseInt( attrs[ 0 ] ); + + if ( isNaN( id ) ) { + + id = attrs[ 0 ]; + + } + + } + + var name = '', type = ''; + + if ( attrs.length > 1 ) { + + name = attrs[ 1 ].replace( /^(\w+)::/, '' ); + type = attrs[ 2 ]; + + } + + return { id: id, name: name, type: type }; + + }, + + parseNodeProperty: function ( line, property, contentLine ) { + + var propName = property[ 1 ].replace( /^"/, '' ).replace( /"$/, '' ).trim(); + var propValue = property[ 2 ].replace( /^"/, '' ).replace( /"$/, '' ).trim(); + + // for special case: base64 image data follows "Content: ," line + // Content: , + // "/9j/4RDaRXhpZgAATU0A..." + if ( propName === 'Content' && propValue === ',' ) { + + propValue = contentLine.replace( /"/g, '' ).replace( /,$/, '' ).trim(); + + } + + var currentNode = this.getCurrentNode(); + var parentName = currentNode.name; + + if ( parentName === 'Properties70' ) { + + this.parseNodeSpecialProperty( line, propName, propValue ); + return; + + } + + // Connections + if ( propName === 'C' ) { + + var connProps = propValue.split( ',' ).slice( 1 ); + var from = parseInt( connProps[ 0 ] ); + var to = parseInt( connProps[ 1 ] ); + + var rest = propValue.split( ',' ).slice( 3 ); + + rest = rest.map( function ( elem ) { + + return elem.trim().replace( /^"/, '' ); + + } ); + + propName = 'connections'; + propValue = [ from, to ]; + append( propValue, rest ); + + if ( currentNode[ propName ] === undefined ) { + + currentNode[ propName ] = []; + + } + + } + + // Node + if ( propName === 'Node' ) currentNode.id = propValue; + + // connections + if ( propName in currentNode && Array.isArray( currentNode[ propName ] ) ) { + + currentNode[ propName ].push( propValue ); + + } else { + + if ( propName !== 'a' ) currentNode[ propName ] = propValue; + else currentNode.a = propValue; + + } + + this.setCurrentProp( currentNode, propName ); + + // convert string to array, unless it ends in ',' in which case more will be added to it + if ( propName === 'a' && propValue.slice( - 1 ) !== ',' ) { + + currentNode.a = parseNumberArray( propValue ); + + } + + }, + + parseNodePropertyContinued: function ( line ) { + + var currentNode = this.getCurrentNode(); + + currentNode.a += line; + + // if the line doesn't end in ',' we have reached the end of the property value + // so convert the string to an array + if ( line.slice( - 1 ) !== ',' ) { + + currentNode.a = parseNumberArray( currentNode.a ); + + } + + }, + + // parse "Property70" + parseNodeSpecialProperty: function ( line, propName, propValue ) { + + // split this + // P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1 + // into array like below + // ["Lcl Scaling", "Lcl Scaling", "", "A", "1,1,1" ] + var props = propValue.split( '",' ).map( function ( prop ) { + + return prop.trim().replace( /^\"/, '' ).replace( /\s/, '_' ); + + } ); + + var innerPropName = props[ 0 ]; + var innerPropType1 = props[ 1 ]; + var innerPropType2 = props[ 2 ]; + var innerPropFlag = props[ 3 ]; + var innerPropValue = props[ 4 ]; + + // cast values where needed, otherwise leave as strings + switch ( innerPropType1 ) { + + case 'int': + case 'enum': + case 'bool': + case 'ULongLong': + case 'double': + case 'Number': + case 'FieldOfView': + innerPropValue = parseFloat( innerPropValue ); + break; + + case 'Color': + case 'ColorRGB': + case 'Vector3D': + case 'Lcl_Translation': + case 'Lcl_Rotation': + case 'Lcl_Scaling': + innerPropValue = parseNumberArray( innerPropValue ); + break; + + } + + // CAUTION: these props must append to parent's parent + this.getPrevNode()[ innerPropName ] = { + + 'type': innerPropType1, + 'type2': innerPropType2, + 'flag': innerPropFlag, + 'value': innerPropValue + + }; + + this.setCurrentProp( this.getPrevNode(), innerPropName ); + + }, + + } ); + + // Parse an FBX file in Binary format + function BinaryParser() {} + + Object.assign( BinaryParser.prototype, { + + parse: function ( buffer ) { + + var reader = new BinaryReader( buffer ); + reader.skip( 23 ); // skip magic 23 bytes + + var version = reader.getUint32(); + + console.log( 'THREE.FBXLoader: FBX binary version: ' + version ); + + var allNodes = new FBXTree(); + + while ( ! this.endOfContent( reader ) ) { + + var node = this.parseNode( reader, version ); + if ( node !== null ) allNodes.add( node.name, node ); + + } + + return allNodes; + + }, + + // Check if reader has reached the end of content. + endOfContent: function ( reader ) { + + // footer size: 160bytes + 16-byte alignment padding + // - 16bytes: magic + // - padding til 16-byte alignment (at least 1byte?) + // (seems like some exporters embed fixed 15 or 16bytes?) + // - 4bytes: magic + // - 4bytes: version + // - 120bytes: zero + // - 16bytes: magic + if ( reader.size() % 16 === 0 ) { + + return ( ( reader.getOffset() + 160 + 16 ) & ~ 0xf ) >= reader.size(); + + } else { + + return reader.getOffset() + 160 + 16 >= reader.size(); + + } + + }, + + // recursively parse nodes until the end of the file is reached + parseNode: function ( reader, version ) { + + var node = {}; + + // The first three data sizes depends on version. + var endOffset = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + var numProperties = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + + // note: do not remove this even if you get a linter warning as it moves the buffer forward + var propertyListLen = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + + var nameLen = reader.getUint8(); + var name = reader.getString( nameLen ); + + // Regards this node as NULL-record if endOffset is zero + if ( endOffset === 0 ) return null; + + var propertyList = []; + + for ( var i = 0; i < numProperties; i ++ ) { + + propertyList.push( this.parseProperty( reader ) ); + + } + + // Regards the first three elements in propertyList as id, attrName, and attrType + var id = propertyList.length > 0 ? propertyList[ 0 ] : ''; + var attrName = propertyList.length > 1 ? propertyList[ 1 ] : ''; + var attrType = propertyList.length > 2 ? propertyList[ 2 ] : ''; + + // check if this node represents just a single property + // like (name, 0) set or (name2, [0, 1, 2]) set of {name: 0, name2: [0, 1, 2]} + node.singleProperty = ( numProperties === 1 && reader.getOffset() === endOffset ) ? true : false; + + while ( endOffset > reader.getOffset() ) { + + var subNode = this.parseNode( reader, version ); + + if ( subNode !== null ) this.parseSubNode( name, node, subNode ); + + } + + node.propertyList = propertyList; // raw property list used by parent + + if ( typeof id === 'number' ) node.id = id; + if ( attrName !== '' ) node.attrName = attrName; + if ( attrType !== '' ) node.attrType = attrType; + if ( name !== '' ) node.name = name; + + return node; + + }, + + parseSubNode: function ( name, node, subNode ) { + + // special case: child node is single property + if ( subNode.singleProperty === true ) { + + var value = subNode.propertyList[ 0 ]; + + if ( Array.isArray( value ) ) { + + node[ subNode.name ] = subNode; + + subNode.a = value; + + } else { + + node[ subNode.name ] = value; + + } + + } else if ( name === 'Connections' && subNode.name === 'C' ) { + + var array = []; + + subNode.propertyList.forEach( function ( property, i ) { + + // first Connection is FBX type (OO, OP, etc.). We'll discard these + if ( i !== 0 ) array.push( property ); + + } ); + + if ( node.connections === undefined ) { + + node.connections = []; + + } + + node.connections.push( array ); + + } else if ( subNode.name === 'Properties70' ) { + + var keys = Object.keys( subNode ); + + keys.forEach( function ( key ) { + + node[ key ] = subNode[ key ]; + + } ); + + } else if ( name === 'Properties70' && subNode.name === 'P' ) { + + var innerPropName = subNode.propertyList[ 0 ]; + var innerPropType1 = subNode.propertyList[ 1 ]; + var innerPropType2 = subNode.propertyList[ 2 ]; + var innerPropFlag = subNode.propertyList[ 3 ]; + var innerPropValue; + + if ( innerPropName.indexOf( 'Lcl ' ) === 0 ) innerPropName = innerPropName.replace( 'Lcl ', 'Lcl_' ); + if ( innerPropType1.indexOf( 'Lcl ' ) === 0 ) innerPropType1 = innerPropType1.replace( 'Lcl ', 'Lcl_' ); + + if ( innerPropType1 === 'Color' || innerPropType1 === 'ColorRGB' || innerPropType1 === 'Vector' || innerPropType1 === 'Vector3D' || innerPropType1.indexOf( 'Lcl_' ) === 0 ) { + + innerPropValue = [ + subNode.propertyList[ 4 ], + subNode.propertyList[ 5 ], + subNode.propertyList[ 6 ] + ]; + + } else { + + innerPropValue = subNode.propertyList[ 4 ]; + + } + + // this will be copied to parent, see above + node[ innerPropName ] = { + + 'type': innerPropType1, + 'type2': innerPropType2, + 'flag': innerPropFlag, + 'value': innerPropValue + + }; + + } else if ( node[ subNode.name ] === undefined ) { + + if ( typeof subNode.id === 'number' ) { + + node[ subNode.name ] = {}; + node[ subNode.name ][ subNode.id ] = subNode; + + } else { + + node[ subNode.name ] = subNode; + + } + + } else { + + if ( subNode.name === 'PoseNode' ) { + + if ( ! Array.isArray( node[ subNode.name ] ) ) { + + node[ subNode.name ] = [ node[ subNode.name ] ]; + + } + + node[ subNode.name ].push( subNode ); + + } else if ( node[ subNode.name ][ subNode.id ] === undefined ) { + + node[ subNode.name ][ subNode.id ] = subNode; + + } + + } + + }, + + parseProperty: function ( reader ) { + + var type = reader.getString( 1 ); + + switch ( type ) { + + case 'C': + return reader.getBoolean(); + + case 'D': + return reader.getFloat64(); + + case 'F': + return reader.getFloat32(); + + case 'I': + return reader.getInt32(); + + case 'L': + return reader.getInt64(); + + case 'R': + var length = reader.getUint32(); + return reader.getArrayBuffer( length ); + + case 'S': + var length = reader.getUint32(); + return reader.getString( length ); + + case 'Y': + return reader.getInt16(); + + case 'b': + case 'c': + case 'd': + case 'f': + case 'i': + case 'l': + + var arrayLength = reader.getUint32(); + var encoding = reader.getUint32(); // 0: non-compressed, 1: compressed + var compressedLength = reader.getUint32(); + + if ( encoding === 0 ) { + + switch ( type ) { + + case 'b': + case 'c': + return reader.getBooleanArray( arrayLength ); + + case 'd': + return reader.getFloat64Array( arrayLength ); + + case 'f': + return reader.getFloat32Array( arrayLength ); + + case 'i': + return reader.getInt32Array( arrayLength ); + + case 'l': + return reader.getInt64Array( arrayLength ); + + } + + } + + if ( window.Zlib === undefined ) { + + console.error( 'THREE.FBXLoader: External library Inflate.min.js required, obtain or import from https://github.com/imaya/zlib.js' ); + + } + + var inflate = new Zlib.Inflate( new Uint8Array( reader.getArrayBuffer( compressedLength ) ) ); // eslint-disable-line no-undef + var reader2 = new BinaryReader( inflate.decompress().buffer ); + + switch ( type ) { + + case 'b': + case 'c': + return reader2.getBooleanArray( arrayLength ); + + case 'd': + return reader2.getFloat64Array( arrayLength ); + + case 'f': + return reader2.getFloat32Array( arrayLength ); + + case 'i': + return reader2.getInt32Array( arrayLength ); + + case 'l': + return reader2.getInt64Array( arrayLength ); + + } + + default: + throw new Error( 'THREE.FBXLoader: Unknown property type ' + type ); + + } + + } + + } ); + + + function BinaryReader( buffer, littleEndian ) { + + this.dv = new DataView( buffer ); + this.offset = 0; + this.littleEndian = ( littleEndian !== undefined ) ? littleEndian : true; + + } + + Object.assign( BinaryReader.prototype, { + + getOffset: function () { + + return this.offset; + + }, + + size: function () { + + return this.dv.buffer.byteLength; + + }, + + skip: function ( length ) { + + this.offset += length; + + }, + + // seems like true/false representation depends on exporter. + // true: 1 or 'Y'(=0x59), false: 0 or 'T'(=0x54) + // then sees LSB. + getBoolean: function () { + + return ( this.getUint8() & 1 ) === 1; + + }, + + getBooleanArray: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getBoolean() ); + + } + + return a; + + }, + + getUint8: function () { + + var value = this.dv.getUint8( this.offset ); + this.offset += 1; + return value; + + }, + + getInt16: function () { + + var value = this.dv.getInt16( this.offset, this.littleEndian ); + this.offset += 2; + return value; + + }, + + getInt32: function () { + + var value = this.dv.getInt32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getInt32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt32() ); + + } + + return a; + + }, + + getUint32: function () { + + var value = this.dv.getUint32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + // JavaScript doesn't support 64-bit integer so calculate this here + // 1 << 32 will return 1 so using multiply operation instead here. + // There's a possibility that this method returns wrong value if the value + // is out of the range between Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER. + // TODO: safely handle 64-bit integer + getInt64: function () { + + var low, high; + + if ( this.littleEndian ) { + + low = this.getUint32(); + high = this.getUint32(); + + } else { + + high = this.getUint32(); + low = this.getUint32(); + + } + + // calculate negative value + if ( high & 0x80000000 ) { + + high = ~ high & 0xFFFFFFFF; + low = ~ low & 0xFFFFFFFF; + + if ( low === 0xFFFFFFFF ) high = ( high + 1 ) & 0xFFFFFFFF; + + low = ( low + 1 ) & 0xFFFFFFFF; + + return - ( high * 0x100000000 + low ); + + } + + return high * 0x100000000 + low; + + }, + + getInt64Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt64() ); + + } + + return a; + + }, + + // Note: see getInt64() comment + getUint64: function () { + + var low, high; + + if ( this.littleEndian ) { + + low = this.getUint32(); + high = this.getUint32(); + + } else { + + high = this.getUint32(); + low = this.getUint32(); + + } + + return high * 0x100000000 + low; + + }, + + getFloat32: function () { + + var value = this.dv.getFloat32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getFloat32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getFloat32() ); + + } + + return a; + + }, + + getFloat64: function () { + + var value = this.dv.getFloat64( this.offset, this.littleEndian ); + this.offset += 8; + return value; + + }, + + getFloat64Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getFloat64() ); + + } + + return a; + + }, + + getArrayBuffer: function ( size ) { + + var value = this.dv.buffer.slice( this.offset, this.offset + size ); + this.offset += size; + return value; + + }, + + getString: function ( size ) { + + var a = new Uint8Array( size ); + + for ( var i = 0; i < size; i ++ ) { + + a[ i ] = this.getUint8(); + + } + + var nullByte = a.indexOf( 0 ); + if ( nullByte >= 0 ) a = a.slice( 0, nullByte ); + + return THREE.LoaderUtils.decodeText( a ); + + } + + } ); + + // FBXTree holds a representation of the FBX data, returned by the TextParser ( FBX ASCII format) + // and BinaryParser( FBX Binary format) + function FBXTree() {} + + Object.assign( FBXTree.prototype, { + + add: function ( key, val ) { + + this[ key ] = val; + + }, + + } ); + + function isFbxFormatBinary( buffer ) { + + var CORRECT = 'Kaydara FBX Binary \0'; + + return buffer.byteLength >= CORRECT.length && CORRECT === convertArrayBufferToString( buffer, 0, CORRECT.length ); + + } + + function isFbxFormatASCII( text ) { + + var CORRECT = [ 'K', 'a', 'y', 'd', 'a', 'r', 'a', '\\', 'F', 'B', 'X', '\\', 'B', 'i', 'n', 'a', 'r', 'y', '\\', '\\' ]; + + var cursor = 0; + + function read( offset ) { + + var result = text[ offset - 1 ]; + text = text.slice( cursor + offset ); + cursor ++; + return result; + + } + + for ( var i = 0; i < CORRECT.length; ++ i ) { + + var num = read( 1 ); + if ( num === CORRECT[ i ] ) { + + return false; + + } + + } + + return true; + + } + + function getFbxVersion( text ) { + + var versionRegExp = /FBXVersion: (\d+)/; + var match = text.match( versionRegExp ); + if ( match ) { + + var version = parseInt( match[ 1 ] ); + return version; + + } + throw new Error( 'THREE.FBXLoader: Cannot find the version number for the file given.' ); + + } + + // Converts FBX ticks into real time seconds. + function convertFBXTimeToSeconds( time ) { + + return time / 46186158000; + + } + + + // Parses comma separated list of numbers and returns them an array. + // Used internally by the TextParser + function parseNumberArray( value ) { + + var array = value.split( ',' ).map( function ( val ) { + + return parseFloat( val ); + + } ); + + return array; + + } + + function convertArrayBufferToString( buffer, from, to ) { + + if ( from === undefined ) from = 0; + if ( to === undefined ) to = buffer.byteLength; + + return THREE.LoaderUtils.decodeText( new Uint8Array( buffer, from, to ) ); + + } + + function append( a, b ) { + + for ( var i = 0, j = a.length, l = b.length; i < l; i ++, j ++ ) { + + a[ j ] = b[ i ]; + + } + + } + + function slice( a, b, from, to ) { + + for ( var i = from, j = 0; i < to; i ++, j ++ ) { + + a[ j ] = b[ i ]; + + } + + return a; + + } + + // inject array a2 into array a1 at index + function inject( a1, index, a2 ) { + + return a1.slice( 0, index ).concat( a2 ).concat( a1.slice( index ) ); + + } + +} )(); + diff --git a/daydream/public/libs/OrbitControls.js b/daydream/public/libs/OrbitControls.js new file mode 100644 index 000000000..eba508c37 --- /dev/null +++ b/daydream/public/libs/OrbitControls.js @@ -0,0 +1,1044 @@ +/** + * @author qiao / https://github.com/qiao + * @author mrdoob / http://mrdoob.com + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author erich666 / http://erichaines.com + */ + +// This set of controls performs orbiting, dollying (zooming), and panning. +// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). +// +// Orbit - left mouse / touch: one-finger move +// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish +// Pan - right mouse, or arrow keys / touch: two-finger move + +THREE.OrbitControls = function ( object, domElement ) { + + this.object = object; + + this.domElement = ( domElement !== undefined ) ? domElement : document; + + // Set to false to disable this control + this.enabled = true; + + // "target" sets the location of focus, where the object orbits around + this.target = new THREE.Vector3(); + + // How far you can dolly in and out ( PerspectiveCamera only ) + this.minDistance = 0; + this.maxDistance = Infinity; + + // How far you can zoom in and out ( OrthographicCamera only ) + this.minZoom = 0; + this.maxZoom = Infinity; + + // How far you can orbit vertically, upper and lower limits. + // Range is 0 to Math.PI radians. + this.minPolarAngle = 0; // radians + this.maxPolarAngle = Math.PI; // radians + + // How far you can orbit horizontally, upper and lower limits. + // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. + this.minAzimuthAngle = - Infinity; // radians + this.maxAzimuthAngle = Infinity; // radians + + // Set to true to enable damping (inertia) + // If damping is enabled, you must call controls.update() in your animation loop + this.enableDamping = false; + this.dampingFactor = 0.25; + + // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. + // Set to false to disable zooming + this.enableZoom = true; + this.zoomSpeed = 1.0; + + // Set to false to disable rotating + this.enableRotate = true; + this.rotateSpeed = 1.0; + + // Set to false to disable panning + this.enablePan = true; + this.panSpeed = 1.0; + this.screenSpacePanning = false; // if true, pan in screen-space + this.keyPanSpeed = 7.0; // pixels moved per arrow key push + + // Set to true to automatically rotate around the target + // If auto-rotate is enabled, you must call controls.update() in your animation loop + this.autoRotate = false; + this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 + + // Set to false to disable use of the keys + this.enableKeys = true; + + // The four arrow keys + this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; + + // Mouse buttons + this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; + + // for reset + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + this.zoom0 = this.object.zoom; + + // + // public methods + // + + this.getPolarAngle = function () { + + return spherical.phi; + + }; + + this.getAzimuthalAngle = function () { + + return spherical.theta; + + }; + + this.saveState = function () { + + scope.target0.copy( scope.target ); + scope.position0.copy( scope.object.position ); + scope.zoom0 = scope.object.zoom; + + }; + + this.reset = function () { + + scope.target.copy( scope.target0 ); + scope.object.position.copy( scope.position0 ); + scope.object.zoom = scope.zoom0; + + scope.object.updateProjectionMatrix(); + scope.dispatchEvent( changeEvent ); + + scope.update(); + + state = STATE.NONE; + + }; + + // this method is exposed, but perhaps it would be better if we can make it private... + this.update = function () { + + var offset = new THREE.Vector3(); + + // so camera.up is the orbit axis + var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); + var quatInverse = quat.clone().inverse(); + + var lastPosition = new THREE.Vector3(); + var lastQuaternion = new THREE.Quaternion(); + + return function update() { + + var position = scope.object.position; + + offset.copy( position ).sub( scope.target ); + + // rotate offset to "y-axis-is-up" space + offset.applyQuaternion( quat ); + + // angle from z-axis around y-axis + spherical.setFromVector3( offset ); + + if ( scope.autoRotate && state === STATE.NONE ) { + + rotateLeft( getAutoRotationAngle() ); + + } + + spherical.theta += sphericalDelta.theta; + spherical.phi += sphericalDelta.phi; + + // restrict theta to be between desired limits + spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) ); + + // restrict phi to be between desired limits + spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); + + spherical.makeSafe(); + + + spherical.radius *= scale; + + // restrict radius to be between desired limits + spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); + + // move target to panned location + scope.target.add( panOffset ); + + offset.setFromSpherical( spherical ); + + // rotate offset back to "camera-up-vector-is-up" space + offset.applyQuaternion( quatInverse ); + + position.copy( scope.target ).add( offset ); + + scope.object.lookAt( scope.target ); + + if ( scope.enableDamping === true ) { + + sphericalDelta.theta *= ( 1 - scope.dampingFactor ); + sphericalDelta.phi *= ( 1 - scope.dampingFactor ); + + panOffset.multiplyScalar( 1 - scope.dampingFactor ); + + } else { + + sphericalDelta.set( 0, 0, 0 ); + + panOffset.set( 0, 0, 0 ); + + } + + scale = 1; + + // update condition is: + // min(camera displacement, camera rotation in radians)^2 > EPS + // using small-angle approximation cos(x/2) = 1 - x^2 / 8 + + if ( zoomChanged || + lastPosition.distanceToSquared( scope.object.position ) > EPS || + 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { + + scope.dispatchEvent( changeEvent ); + + lastPosition.copy( scope.object.position ); + lastQuaternion.copy( scope.object.quaternion ); + zoomChanged = false; + + return true; + + } + + return false; + + }; + + }(); + + this.dispose = function () { + + scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false ); + scope.domElement.removeEventListener( 'mousedown', onMouseDown, false ); + scope.domElement.removeEventListener( 'wheel', onMouseWheel, false ); + + scope.domElement.removeEventListener( 'touchstart', onTouchStart, false ); + scope.domElement.removeEventListener( 'touchend', onTouchEnd, false ); + scope.domElement.removeEventListener( 'touchmove', onTouchMove, false ); + + document.removeEventListener( 'mousemove', onMouseMove, false ); + document.removeEventListener( 'mouseup', onMouseUp, false ); + + window.removeEventListener( 'keydown', onKeyDown, false ); + + //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? + + }; + + // + // internals + // + + var scope = this; + + var changeEvent = { type: 'change' }; + var startEvent = { type: 'start' }; + var endEvent = { type: 'end' }; + + var STATE = { NONE: - 1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY_PAN: 4 }; + + var state = STATE.NONE; + + var EPS = 0.000001; + + // current position in spherical coordinates + var spherical = new THREE.Spherical(); + var sphericalDelta = new THREE.Spherical(); + + var scale = 1; + var panOffset = new THREE.Vector3(); + var zoomChanged = false; + + var rotateStart = new THREE.Vector2(); + var rotateEnd = new THREE.Vector2(); + var rotateDelta = new THREE.Vector2(); + + var panStart = new THREE.Vector2(); + var panEnd = new THREE.Vector2(); + var panDelta = new THREE.Vector2(); + + var dollyStart = new THREE.Vector2(); + var dollyEnd = new THREE.Vector2(); + var dollyDelta = new THREE.Vector2(); + + function getAutoRotationAngle() { + + return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; + + } + + function getZoomScale() { + + return Math.pow( 0.95, scope.zoomSpeed ); + + } + + function rotateLeft( angle ) { + + sphericalDelta.theta -= angle; + + } + + function rotateUp( angle ) { + + sphericalDelta.phi -= angle; + + } + + var panLeft = function () { + + var v = new THREE.Vector3(); + + return function panLeft( distance, objectMatrix ) { + + v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix + v.multiplyScalar( - distance ); + + panOffset.add( v ); + + }; + + }(); + + var panUp = function () { + + var v = new THREE.Vector3(); + + return function panUp( distance, objectMatrix ) { + + if ( scope.screenSpacePanning === true ) { + + v.setFromMatrixColumn( objectMatrix, 1 ); + + } else { + + v.setFromMatrixColumn( objectMatrix, 0 ); + v.crossVectors( scope.object.up, v ); + + } + + v.multiplyScalar( distance ); + + panOffset.add( v ); + + }; + + }(); + + // deltaX and deltaY are in pixels; right and down are positive + var pan = function () { + + var offset = new THREE.Vector3(); + + return function pan( deltaX, deltaY ) { + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + if ( scope.object.isPerspectiveCamera ) { + + // perspective + var position = scope.object.position; + offset.copy( position ).sub( scope.target ); + var targetDistance = offset.length(); + + // half of the fov is center to top of screen + targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); + + // we use only clientHeight here so aspect ratio does not distort speed + panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); + panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); + + } else if ( scope.object.isOrthographicCamera ) { + + // orthographic + panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); + panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); + + } else { + + // camera neither orthographic nor perspective + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); + scope.enablePan = false; + + } + + }; + + }(); + + function dollyIn( dollyScale ) { + + if ( scope.object.isPerspectiveCamera ) { + + scale /= dollyScale; + + } else if ( scope.object.isOrthographicCamera ) { + + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + function dollyOut( dollyScale ) { + + if ( scope.object.isPerspectiveCamera ) { + + scale *= dollyScale; + + } else if ( scope.object.isOrthographicCamera ) { + + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + // + // event callbacks - update the object state + // + + function handleMouseDownRotate( event ) { + + //console.log( 'handleMouseDownRotate' ); + + rotateStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownDolly( event ) { + + //console.log( 'handleMouseDownDolly' ); + + dollyStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownPan( event ) { + + //console.log( 'handleMouseDownPan' ); + + panStart.set( event.clientX, event.clientY ); + + } + + function handleMouseMoveRotate( event ) { + + //console.log( 'handleMouseMoveRotate' ); + + rotateEnd.set( event.clientX, event.clientY ); + + rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + // rotating across whole screen goes 360 degrees around + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth ); + + // rotating up and down along whole screen attempts to go 360, but limited to 180 + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); + + rotateStart.copy( rotateEnd ); + + scope.update(); + + } + + function handleMouseMoveDolly( event ) { + + //console.log( 'handleMouseMoveDolly' ); + + dollyEnd.set( event.clientX, event.clientY ); + + dollyDelta.subVectors( dollyEnd, dollyStart ); + + if ( dollyDelta.y > 0 ) { + + dollyIn( getZoomScale() ); + + } else if ( dollyDelta.y < 0 ) { + + dollyOut( getZoomScale() ); + + } + + dollyStart.copy( dollyEnd ); + + scope.update(); + + } + + function handleMouseMovePan( event ) { + + //console.log( 'handleMouseMovePan' ); + + panEnd.set( event.clientX, event.clientY ); + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + scope.update(); + + } + + function handleMouseUp( event ) { + + // console.log( 'handleMouseUp' ); + + } + + function handleMouseWheel( event ) { + + // console.log( 'handleMouseWheel' ); + + if ( event.deltaY < 0 ) { + + dollyOut( getZoomScale() ); + + } else if ( event.deltaY > 0 ) { + + dollyIn( getZoomScale() ); + + } + + scope.update(); + + } + + function handleKeyDown( event ) { + + //console.log( 'handleKeyDown' ); + + switch ( event.keyCode ) { + + case scope.keys.UP: + pan( 0, scope.keyPanSpeed ); + scope.update(); + break; + + case scope.keys.BOTTOM: + pan( 0, - scope.keyPanSpeed ); + scope.update(); + break; + + case scope.keys.LEFT: + pan( scope.keyPanSpeed, 0 ); + scope.update(); + break; + + case scope.keys.RIGHT: + pan( - scope.keyPanSpeed, 0 ); + scope.update(); + break; + + } + + } + + function handleTouchStartRotate( event ) { + + //console.log( 'handleTouchStartRotate' ); + + rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + + } + + function handleTouchStartDollyPan( event ) { + + //console.log( 'handleTouchStartDollyPan' ); + + if ( scope.enableZoom ) { + + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + + var distance = Math.sqrt( dx * dx + dy * dy ); + + dollyStart.set( 0, distance ); + + } + + if ( scope.enablePan ) { + + var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); + var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); + + panStart.set( x, y ); + + } + + } + + function handleTouchMoveRotate( event ) { + + //console.log( 'handleTouchMoveRotate' ); + + rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + + rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + // rotating across whole screen goes 360 degrees around + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth ); + + // rotating up and down along whole screen attempts to go 360, but limited to 180 + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); + + rotateStart.copy( rotateEnd ); + + scope.update(); + + } + + function handleTouchMoveDollyPan( event ) { + + //console.log( 'handleTouchMoveDollyPan' ); + + if ( scope.enableZoom ) { + + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + + var distance = Math.sqrt( dx * dx + dy * dy ); + + dollyEnd.set( 0, distance ); + + dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); + + dollyIn( dollyDelta.y ); + + dollyStart.copy( dollyEnd ); + + } + + if ( scope.enablePan ) { + + var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); + var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); + + panEnd.set( x, y ); + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + } + + scope.update(); + + } + + function handleTouchEnd( event ) { + + //console.log( 'handleTouchEnd' ); + + } + + // + // event handlers - FSM: listen for events and reset state + // + + function onMouseDown( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + switch ( event.button ) { + + case scope.mouseButtons.ORBIT: + + if ( scope.enableRotate === false ) return; + + handleMouseDownRotate( event ); + + state = STATE.ROTATE; + + break; + + case scope.mouseButtons.ZOOM: + + if ( scope.enableZoom === false ) return; + + handleMouseDownDolly( event ); + + state = STATE.DOLLY; + + break; + + case scope.mouseButtons.PAN: + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + break; + + } + + if ( state !== STATE.NONE ) { + + document.addEventListener( 'mousemove', onMouseMove, false ); + document.addEventListener( 'mouseup', onMouseUp, false ); + + scope.dispatchEvent( startEvent ); + + } + + } + + function onMouseMove( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + switch ( state ) { + + case STATE.ROTATE: + + if ( scope.enableRotate === false ) return; + + handleMouseMoveRotate( event ); + + break; + + case STATE.DOLLY: + + if ( scope.enableZoom === false ) return; + + handleMouseMoveDolly( event ); + + break; + + case STATE.PAN: + + if ( scope.enablePan === false ) return; + + handleMouseMovePan( event ); + + break; + + } + + } + + function onMouseUp( event ) { + + if ( scope.enabled === false ) return; + + handleMouseUp( event ); + + document.removeEventListener( 'mousemove', onMouseMove, false ); + document.removeEventListener( 'mouseup', onMouseUp, false ); + + scope.dispatchEvent( endEvent ); + + state = STATE.NONE; + + } + + function onMouseWheel( event ) { + + if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return; + + event.preventDefault(); + event.stopPropagation(); + + scope.dispatchEvent( startEvent ); + + handleMouseWheel( event ); + + scope.dispatchEvent( endEvent ); + + } + + function onKeyDown( event ) { + + if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; + + handleKeyDown( event ); + + } + + function onTouchStart( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + switch ( event.touches.length ) { + + case 1: // one-fingered touch: rotate + + if ( scope.enableRotate === false ) return; + + handleTouchStartRotate( event ); + + state = STATE.TOUCH_ROTATE; + + break; + + case 2: // two-fingered touch: dolly-pan + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + + handleTouchStartDollyPan( event ); + + state = STATE.TOUCH_DOLLY_PAN; + + break; + + default: + + state = STATE.NONE; + + } + + if ( state !== STATE.NONE ) { + + scope.dispatchEvent( startEvent ); + + } + + } + + function onTouchMove( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + switch ( event.touches.length ) { + + case 1: // one-fingered touch: rotate + + if ( scope.enableRotate === false ) return; + if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed? + + handleTouchMoveRotate( event ); + + break; + + case 2: // two-fingered touch: dolly-pan + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + if ( state !== STATE.TOUCH_DOLLY_PAN ) return; // is this needed? + + handleTouchMoveDollyPan( event ); + + break; + + default: + + state = STATE.NONE; + + } + + } + + function onTouchEnd( event ) { + + if ( scope.enabled === false ) return; + + handleTouchEnd( event ); + + scope.dispatchEvent( endEvent ); + + state = STATE.NONE; + + } + + function onContextMenu( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + } + + // + + scope.domElement.addEventListener( 'contextmenu', onContextMenu, false ); + + scope.domElement.addEventListener( 'mousedown', onMouseDown, false ); + scope.domElement.addEventListener( 'wheel', onMouseWheel, false ); + + scope.domElement.addEventListener( 'touchstart', onTouchStart, false ); + scope.domElement.addEventListener( 'touchend', onTouchEnd, false ); + scope.domElement.addEventListener( 'touchmove', onTouchMove, false ); + + window.addEventListener( 'keydown', onKeyDown, false ); + + // force an update at start + + this.update(); + +}; + +THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); +THREE.OrbitControls.prototype.constructor = THREE.OrbitControls; + +Object.defineProperties( THREE.OrbitControls.prototype, { + + center: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .center has been renamed to .target' ); + return this.target; + + } + + }, + + // backward compatibility + + noZoom: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); + return ! this.enableZoom; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); + this.enableZoom = ! value; + + } + + }, + + noRotate: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); + return ! this.enableRotate; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); + this.enableRotate = ! value; + + } + + }, + + noPan: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); + return ! this.enablePan; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); + this.enablePan = ! value; + + } + + }, + + noKeys: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); + return ! this.enableKeys; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); + this.enableKeys = ! value; + + } + + }, + + staticMoving: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); + return ! this.enableDamping; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); + this.enableDamping = ! value; + + } + + }, + + dynamicDampingFactor: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); + return this.dampingFactor; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); + this.dampingFactor = value; + + } + + } + +} ); + diff --git a/daydream/public/libs/cannon.js b/daydream/public/libs/cannon.js new file mode 100644 index 000000000..331ecc5fe --- /dev/null +++ b/daydream/public/libs/cannon.js @@ -0,0 +1,13687 @@ +/* + * Copyright (c) 2015 cannon.js Authors + * + * 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(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&false)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.CANNON=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o (http://steffe.se)", + "keywords": [ + "cannon.js", + "cannon", + "physics", + "engine", + "3d" + ], + "main": "./build/cannon.js", + "engines": { + "node": "*" + }, + "repository": { + "type": "git", + "url": "https://github.com/schteppe/cannon.js.git" + }, + "bugs": { + "url": "https://github.com/schteppe/cannon.js/issues" + }, + "licenses": [ + { + "type": "MIT" + } + ], + "devDependencies": { + "jshint": "latest", + "uglify-js": "latest", + "nodeunit": "^0.9.0", + "grunt": "~0.4.0", + "grunt-contrib-jshint": "~0.1.1", + "grunt-contrib-nodeunit": "^0.4.1", + "grunt-contrib-concat": "~0.1.3", + "grunt-contrib-uglify": "^0.5.1", + "grunt-browserify": "^2.1.4", + "grunt-contrib-yuidoc": "^0.5.2", + "browserify": "*" + }, + "dependencies": {} +} + +},{}],2:[function(_dereq_,module,exports){ +// Export classes +module.exports = { + version : _dereq_('../package.json').version, + + AABB : _dereq_('./collision/AABB'), + ArrayCollisionMatrix : _dereq_('./collision/ArrayCollisionMatrix'), + Body : _dereq_('./objects/Body'), + Box : _dereq_('./shapes/Box'), + Broadphase : _dereq_('./collision/Broadphase'), + Constraint : _dereq_('./constraints/Constraint'), + ContactEquation : _dereq_('./equations/ContactEquation'), + Narrowphase : _dereq_('./world/Narrowphase'), + ConeTwistConstraint : _dereq_('./constraints/ConeTwistConstraint'), + ContactMaterial : _dereq_('./material/ContactMaterial'), + ConvexPolyhedron : _dereq_('./shapes/ConvexPolyhedron'), + Cylinder : _dereq_('./shapes/Cylinder'), + DistanceConstraint : _dereq_('./constraints/DistanceConstraint'), + Equation : _dereq_('./equations/Equation'), + EventTarget : _dereq_('./utils/EventTarget'), + FrictionEquation : _dereq_('./equations/FrictionEquation'), + GSSolver : _dereq_('./solver/GSSolver'), + GridBroadphase : _dereq_('./collision/GridBroadphase'), + Heightfield : _dereq_('./shapes/Heightfield'), + HingeConstraint : _dereq_('./constraints/HingeConstraint'), + LockConstraint : _dereq_('./constraints/LockConstraint'), + Mat3 : _dereq_('./math/Mat3'), + Material : _dereq_('./material/Material'), + NaiveBroadphase : _dereq_('./collision/NaiveBroadphase'), + ObjectCollisionMatrix : _dereq_('./collision/ObjectCollisionMatrix'), + Pool : _dereq_('./utils/Pool'), + Particle : _dereq_('./shapes/Particle'), + Plane : _dereq_('./shapes/Plane'), + PointToPointConstraint : _dereq_('./constraints/PointToPointConstraint'), + Quaternion : _dereq_('./math/Quaternion'), + Ray : _dereq_('./collision/Ray'), + RaycastVehicle : _dereq_('./objects/RaycastVehicle'), + RaycastResult : _dereq_('./collision/RaycastResult'), + RigidVehicle : _dereq_('./objects/RigidVehicle'), + RotationalEquation : _dereq_('./equations/RotationalEquation'), + RotationalMotorEquation : _dereq_('./equations/RotationalMotorEquation'), + SAPBroadphase : _dereq_('./collision/SAPBroadphase'), + SPHSystem : _dereq_('./objects/SPHSystem'), + Shape : _dereq_('./shapes/Shape'), + Solver : _dereq_('./solver/Solver'), + Sphere : _dereq_('./shapes/Sphere'), + SplitSolver : _dereq_('./solver/SplitSolver'), + Spring : _dereq_('./objects/Spring'), + Trimesh : _dereq_('./shapes/Trimesh'), + Vec3 : _dereq_('./math/Vec3'), + Vec3Pool : _dereq_('./utils/Vec3Pool'), + World : _dereq_('./world/World'), +}; + +},{"../package.json":1,"./collision/AABB":3,"./collision/ArrayCollisionMatrix":4,"./collision/Broadphase":5,"./collision/GridBroadphase":6,"./collision/NaiveBroadphase":7,"./collision/ObjectCollisionMatrix":8,"./collision/Ray":9,"./collision/RaycastResult":10,"./collision/SAPBroadphase":11,"./constraints/ConeTwistConstraint":12,"./constraints/Constraint":13,"./constraints/DistanceConstraint":14,"./constraints/HingeConstraint":15,"./constraints/LockConstraint":16,"./constraints/PointToPointConstraint":17,"./equations/ContactEquation":19,"./equations/Equation":20,"./equations/FrictionEquation":21,"./equations/RotationalEquation":22,"./equations/RotationalMotorEquation":23,"./material/ContactMaterial":24,"./material/Material":25,"./math/Mat3":27,"./math/Quaternion":28,"./math/Vec3":30,"./objects/Body":31,"./objects/RaycastVehicle":32,"./objects/RigidVehicle":33,"./objects/SPHSystem":34,"./objects/Spring":35,"./shapes/Box":37,"./shapes/ConvexPolyhedron":38,"./shapes/Cylinder":39,"./shapes/Heightfield":40,"./shapes/Particle":41,"./shapes/Plane":42,"./shapes/Shape":43,"./shapes/Sphere":44,"./shapes/Trimesh":45,"./solver/GSSolver":46,"./solver/Solver":47,"./solver/SplitSolver":48,"./utils/EventTarget":49,"./utils/Pool":51,"./utils/Vec3Pool":54,"./world/Narrowphase":55,"./world/World":56}],3:[function(_dereq_,module,exports){ +var Vec3 = _dereq_('../math/Vec3'); +var Utils = _dereq_('../utils/Utils'); + +module.exports = AABB; + +/** + * Axis aligned bounding box class. + * @class AABB + * @constructor + * @param {Object} [options] + * @param {Vec3} [options.upperBound] + * @param {Vec3} [options.lowerBound] + */ +function AABB(options){ + options = options || {}; + + /** + * The lower bound of the bounding box. + * @property lowerBound + * @type {Vec3} + */ + this.lowerBound = new Vec3(); + if(options.lowerBound){ + this.lowerBound.copy(options.lowerBound); + } + + /** + * The upper bound of the bounding box. + * @property upperBound + * @type {Vec3} + */ + this.upperBound = new Vec3(); + if(options.upperBound){ + this.upperBound.copy(options.upperBound); + } +} + +var tmp = new Vec3(); + +/** + * Set the AABB bounds from a set of points. + * @method setFromPoints + * @param {Array} points An array of Vec3's. + * @param {Vec3} position + * @param {Quaternion} quaternion + * @param {number} skinSize + * @return {AABB} The self object + */ +AABB.prototype.setFromPoints = function(points, position, quaternion, skinSize){ + var l = this.lowerBound, + u = this.upperBound, + q = quaternion; + + // Set to the first point + l.copy(points[0]); + if(q){ + q.vmult(l, l); + } + u.copy(l); + + for(var i = 1; i u.x){ u.x = p.x; } + if(p.x < l.x){ l.x = p.x; } + if(p.y > u.y){ u.y = p.y; } + if(p.y < l.y){ l.y = p.y; } + if(p.z > u.z){ u.z = p.z; } + if(p.z < l.z){ l.z = p.z; } + } + + // Add offset + if (position) { + position.vadd(l, l); + position.vadd(u, u); + } + + if(skinSize){ + l.x -= skinSize; + l.y -= skinSize; + l.z -= skinSize; + u.x += skinSize; + u.y += skinSize; + u.z += skinSize; + } + + return this; +}; + +/** + * Copy bounds from an AABB to this AABB + * @method copy + * @param {AABB} aabb Source to copy from + * @return {AABB} The this object, for chainability + */ +AABB.prototype.copy = function(aabb){ + this.lowerBound.copy(aabb.lowerBound); + this.upperBound.copy(aabb.upperBound); + return this; +}; + +/** + * Clone an AABB + * @method clone + */ +AABB.prototype.clone = function(){ + return new AABB().copy(this); +}; + +/** + * Extend this AABB so that it covers the given AABB too. + * @method extend + * @param {AABB} aabb + */ +AABB.prototype.extend = function(aabb){ + // Extend lower bound + var l = aabb.lowerBound.x; + if(this.lowerBound.x > l){ + this.lowerBound.x = l; + } + + // Upper + var u = aabb.upperBound.x; + if(this.upperBound.x < u){ + this.upperBound.x = u; + } + + // Extend lower bound + var l = aabb.lowerBound.y; + if(this.lowerBound.y > l){ + this.lowerBound.y = l; + } + + // Upper + var u = aabb.upperBound.y; + if(this.upperBound.y < u){ + this.upperBound.y = u; + } + + // Extend lower bound + var l = aabb.lowerBound.z; + if(this.lowerBound.z > l){ + this.lowerBound.z = l; + } + + // Upper + var u = aabb.upperBound.z; + if(this.upperBound.z < u){ + this.upperBound.z = u; + } +}; + +/** + * Returns true if the given AABB overlaps this AABB. + * @method overlaps + * @param {AABB} aabb + * @return {Boolean} + */ +AABB.prototype.overlaps = function(aabb){ + var l1 = this.lowerBound, + u1 = this.upperBound, + l2 = aabb.lowerBound, + u2 = aabb.upperBound; + + // l2 u2 + // |---------| + // |--------| + // l1 u1 + + return ((l2.x <= u1.x && u1.x <= u2.x) || (l1.x <= u2.x && u2.x <= u1.x)) && + ((l2.y <= u1.y && u1.y <= u2.y) || (l1.y <= u2.y && u2.y <= u1.y)) && + ((l2.z <= u1.z && u1.z <= u2.z) || (l1.z <= u2.z && u2.z <= u1.z)); +}; + +/** + * Returns true if the given AABB is fully contained in this AABB. + * @method contains + * @param {AABB} aabb + * @return {Boolean} + */ +AABB.prototype.contains = function(aabb){ + var l1 = this.lowerBound, + u1 = this.upperBound, + l2 = aabb.lowerBound, + u2 = aabb.upperBound; + + // l2 u2 + // |---------| + // |---------------| + // l1 u1 + + return ( + (l1.x <= l2.x && u1.x >= u2.x) && + (l1.y <= l2.y && u1.y >= u2.y) && + (l1.z <= l2.z && u1.z >= u2.z) + ); +}; + +/** + * @method getCorners + * @param {Vec3} a + * @param {Vec3} b + * @param {Vec3} c + * @param {Vec3} d + * @param {Vec3} e + * @param {Vec3} f + * @param {Vec3} g + * @param {Vec3} h + */ +AABB.prototype.getCorners = function(a, b, c, d, e, f, g, h){ + var l = this.lowerBound, + u = this.upperBound; + + a.copy(l); + b.set( u.x, l.y, l.z ); + c.set( u.x, u.y, l.z ); + d.set( l.x, u.y, u.z ); + e.set( u.x, l.y, l.z ); + f.set( l.x, u.y, l.z ); + g.set( l.x, l.y, u.z ); + h.copy(u); +}; + +var transformIntoFrame_corners = [ + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3() +]; + +/** + * Get the representation of an AABB in another frame. + * @method toLocalFrame + * @param {Transform} frame + * @param {AABB} target + * @return {AABB} The "target" AABB object. + */ +AABB.prototype.toLocalFrame = function(frame, target){ + + var corners = transformIntoFrame_corners; + var a = corners[0]; + var b = corners[1]; + var c = corners[2]; + var d = corners[3]; + var e = corners[4]; + var f = corners[5]; + var g = corners[6]; + var h = corners[7]; + + // Get corners in current frame + this.getCorners(a, b, c, d, e, f, g, h); + + // Transform them to new local frame + for(var i=0; i !== 8; i++){ + var corner = corners[i]; + frame.pointToLocal(corner, corner); + } + + return target.setFromPoints(corners); +}; + +/** + * Get the representation of an AABB in the global frame. + * @method toWorldFrame + * @param {Transform} frame + * @param {AABB} target + * @return {AABB} The "target" AABB object. + */ +AABB.prototype.toWorldFrame = function(frame, target){ + + var corners = transformIntoFrame_corners; + var a = corners[0]; + var b = corners[1]; + var c = corners[2]; + var d = corners[3]; + var e = corners[4]; + var f = corners[5]; + var g = corners[6]; + var h = corners[7]; + + // Get corners in current frame + this.getCorners(a, b, c, d, e, f, g, h); + + // Transform them to new local frame + for(var i=0; i !== 8; i++){ + var corner = corners[i]; + frame.pointToWorld(corner, corner); + } + + return target.setFromPoints(corners); +}; + +},{"../math/Vec3":30,"../utils/Utils":53}],4:[function(_dereq_,module,exports){ +module.exports = ArrayCollisionMatrix; + +/** + * Collision "matrix". It's actually a triangular-shaped array of whether two bodies are touching this step, for reference next step + * @class ArrayCollisionMatrix + * @constructor + */ +function ArrayCollisionMatrix() { + + /** + * The matrix storage + * @property matrix + * @type {Array} + */ + this.matrix = []; +} + +/** + * Get an element + * @method get + * @param {Number} i + * @param {Number} j + * @return {Number} + */ +ArrayCollisionMatrix.prototype.get = function(i, j) { + i = i.index; + j = j.index; + if (j > i) { + var temp = j; + j = i; + i = temp; + } + return this.matrix[(i*(i + 1)>>1) + j-1]; +}; + +/** + * Set an element + * @method set + * @param {Number} i + * @param {Number} j + * @param {Number} value + */ +ArrayCollisionMatrix.prototype.set = function(i, j, value) { + i = i.index; + j = j.index; + if (j > i) { + var temp = j; + j = i; + i = temp; + } + this.matrix[(i*(i + 1)>>1) + j-1] = value ? 1 : 0; +}; + +/** + * Sets all elements to zero + * @method reset + */ +ArrayCollisionMatrix.prototype.reset = function() { + for (var i=0, l=this.matrix.length; i!==l; i++) { + this.matrix[i]=0; + } +}; + +/** + * Sets the max number of objects + * @method setNumObjects + * @param {Number} n + */ +ArrayCollisionMatrix.prototype.setNumObjects = function(n) { + this.matrix.length = n*(n-1)>>1; +}; + +},{}],5:[function(_dereq_,module,exports){ +var Body = _dereq_('../objects/Body'); +var Vec3 = _dereq_('../math/Vec3'); +var Quaternion = _dereq_('../math/Quaternion'); +var Shape = _dereq_('../shapes/Shape'); +var Plane = _dereq_('../shapes/Plane'); + +module.exports = Broadphase; + +/** + * Base class for broadphase implementations + * @class Broadphase + * @constructor + * @author schteppe + */ +function Broadphase(){ + /** + * The world to search for collisions in. + * @property world + * @type {World} + */ + this.world = null; + + /** + * If set to true, the broadphase uses bounding boxes for intersection test, else it uses bounding spheres. + * @property useBoundingBoxes + * @type {Boolean} + */ + this.useBoundingBoxes = false; + + /** + * Set to true if the objects in the world moved. + * @property {Boolean} dirty + */ + this.dirty = true; +} + +/** + * Get the collision pairs from the world + * @method collisionPairs + * @param {World} world The world to search in + * @param {Array} p1 Empty array to be filled with body objects + * @param {Array} p2 Empty array to be filled with body objects + */ +Broadphase.prototype.collisionPairs = function(world,p1,p2){ + throw new Error("collisionPairs not implemented for this BroadPhase class!"); +}; + +/** + * Check if a body pair needs to be intersection tested at all. + * @method needBroadphaseCollision + * @param {Body} bodyA + * @param {Body} bodyB + * @return {bool} + */ +var Broadphase_needBroadphaseCollision_STATIC_OR_KINEMATIC = Body.STATIC | Body.KINEMATIC; +Broadphase.prototype.needBroadphaseCollision = function(bodyA,bodyB){ + + // Check collision filter masks + if( (bodyA.collisionFilterGroup & bodyB.collisionFilterMask)===0 || (bodyB.collisionFilterGroup & bodyA.collisionFilterMask)===0){ + return false; + } + + // Check types + if(((bodyA.type & Broadphase_needBroadphaseCollision_STATIC_OR_KINEMATIC)!==0 || bodyA.sleepState === Body.SLEEPING) && + ((bodyB.type & Broadphase_needBroadphaseCollision_STATIC_OR_KINEMATIC)!==0 || bodyB.sleepState === Body.SLEEPING)) { + // Both bodies are static, kinematic or sleeping. Skip. + return false; + } + + return true; +}; + +/** + * Check if the bounding volumes of two bodies intersect. + * @method intersectionTest + * @param {Body} bodyA + * @param {Body} bodyB + * @param {array} pairs1 + * @param {array} pairs2 + */ +Broadphase.prototype.intersectionTest = function(bodyA, bodyB, pairs1, pairs2){ + if(this.useBoundingBoxes){ + this.doBoundingBoxBroadphase(bodyA,bodyB,pairs1,pairs2); + } else { + this.doBoundingSphereBroadphase(bodyA,bodyB,pairs1,pairs2); + } +}; + +/** + * Check if the bounding spheres of two bodies are intersecting. + * @method doBoundingSphereBroadphase + * @param {Body} bodyA + * @param {Body} bodyB + * @param {Array} pairs1 bodyA is appended to this array if intersection + * @param {Array} pairs2 bodyB is appended to this array if intersection + */ +var Broadphase_collisionPairs_r = new Vec3(), // Temp objects + Broadphase_collisionPairs_normal = new Vec3(), + Broadphase_collisionPairs_quat = new Quaternion(), + Broadphase_collisionPairs_relpos = new Vec3(); +Broadphase.prototype.doBoundingSphereBroadphase = function(bodyA,bodyB,pairs1,pairs2){ + var r = Broadphase_collisionPairs_r; + bodyB.position.vsub(bodyA.position,r); + var boundingRadiusSum2 = Math.pow(bodyA.boundingRadius + bodyB.boundingRadius, 2); + var norm2 = r.norm2(); + if(norm2 < boundingRadiusSum2){ + pairs1.push(bodyA); + pairs2.push(bodyB); + } +}; + +/** + * Check if the bounding boxes of two bodies are intersecting. + * @method doBoundingBoxBroadphase + * @param {Body} bodyA + * @param {Body} bodyB + * @param {Array} pairs1 + * @param {Array} pairs2 + */ +Broadphase.prototype.doBoundingBoxBroadphase = function(bodyA,bodyB,pairs1,pairs2){ + if(bodyA.aabbNeedsUpdate){ + bodyA.computeAABB(); + } + if(bodyB.aabbNeedsUpdate){ + bodyB.computeAABB(); + } + + // Check AABB / AABB + if(bodyA.aabb.overlaps(bodyB.aabb)){ + pairs1.push(bodyA); + pairs2.push(bodyB); + } +}; + +/** + * Removes duplicate pairs from the pair arrays. + * @method makePairsUnique + * @param {Array} pairs1 + * @param {Array} pairs2 + */ +var Broadphase_makePairsUnique_temp = { keys:[] }, + Broadphase_makePairsUnique_p1 = [], + Broadphase_makePairsUnique_p2 = []; +Broadphase.prototype.makePairsUnique = function(pairs1,pairs2){ + var t = Broadphase_makePairsUnique_temp, + p1 = Broadphase_makePairsUnique_p1, + p2 = Broadphase_makePairsUnique_p2, + N = pairs1.length; + + for(var i=0; i!==N; i++){ + p1[i] = pairs1[i]; + p2[i] = pairs2[i]; + } + + pairs1.length = 0; + pairs2.length = 0; + + for(var i=0; i!==N; i++){ + var id1 = p1[i].id, + id2 = p2[i].id; + var key = id1 < id2 ? id1+","+id2 : id2+","+id1; + t[key] = i; + t.keys.push(key); + } + + for(var i=0; i!==t.keys.length; i++){ + var key = t.keys.pop(), + pairIndex = t[key]; + pairs1.push(p1[pairIndex]); + pairs2.push(p2[pairIndex]); + delete t[key]; + } +}; + +/** + * To be implemented by subcasses + * @method setWorld + * @param {World} world + */ +Broadphase.prototype.setWorld = function(world){ +}; + +/** + * Check if the bounding spheres of two bodies overlap. + * @method boundingSphereCheck + * @param {Body} bodyA + * @param {Body} bodyB + * @return {boolean} + */ +var bsc_dist = new Vec3(); +Broadphase.boundingSphereCheck = function(bodyA,bodyB){ + var dist = bsc_dist; + bodyA.position.vsub(bodyB.position,dist); + return Math.pow(bodyA.shape.boundingSphereRadius + bodyB.shape.boundingSphereRadius,2) > dist.norm2(); +}; + +/** + * Returns all the bodies within the AABB. + * @method aabbQuery + * @param {World} world + * @param {AABB} aabb + * @param {array} result An array to store resulting bodies in. + * @return {array} + */ +Broadphase.prototype.aabbQuery = function(world, aabb, result){ + console.warn('.aabbQuery is not implemented in this Broadphase subclass.'); + return []; +}; +},{"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"../shapes/Plane":42,"../shapes/Shape":43}],6:[function(_dereq_,module,exports){ +module.exports = GridBroadphase; + +var Broadphase = _dereq_('./Broadphase'); +var Vec3 = _dereq_('../math/Vec3'); +var Shape = _dereq_('../shapes/Shape'); + +/** + * Axis aligned uniform grid broadphase. + * @class GridBroadphase + * @constructor + * @extends Broadphase + * @todo Needs support for more than just planes and spheres. + * @param {Vec3} aabbMin + * @param {Vec3} aabbMax + * @param {Number} nx Number of boxes along x + * @param {Number} ny Number of boxes along y + * @param {Number} nz Number of boxes along z + */ +function GridBroadphase(aabbMin,aabbMax,nx,ny,nz){ + Broadphase.apply(this); + this.nx = nx || 10; + this.ny = ny || 10; + this.nz = nz || 10; + this.aabbMin = aabbMin || new Vec3(100,100,100); + this.aabbMax = aabbMax || new Vec3(-100,-100,-100); + var nbins = this.nx * this.ny * this.nz; + if (nbins <= 0) { + throw "GridBroadphase: Each dimension's n must be >0"; + } + this.bins = []; + this.binLengths = []; //Rather than continually resizing arrays (thrashing the memory), just record length and allow them to grow + this.bins.length = nbins; + this.binLengths.length = nbins; + for (var i=0;i= nx) { xoff0 = nx - 1; } + if (yoff0 < 0) { yoff0 = 0; } else if (yoff0 >= ny) { yoff0 = ny - 1; } + if (zoff0 < 0) { zoff0 = 0; } else if (zoff0 >= nz) { zoff0 = nz - 1; } + if (xoff1 < 0) { xoff1 = 0; } else if (xoff1 >= nx) { xoff1 = nx - 1; } + if (yoff1 < 0) { yoff1 = 0; } else if (yoff1 >= ny) { yoff1 = ny - 1; } + if (zoff1 < 0) { zoff1 = 0; } else if (zoff1 >= nz) { zoff1 = nz - 1; } + + xoff0 *= xstep; + yoff0 *= ystep; + zoff0 *= zstep; + xoff1 *= xstep; + yoff1 *= ystep; + zoff1 *= zstep; + + for (var xoff = xoff0; xoff <= xoff1; xoff += xstep) { + for (var yoff = yoff0; yoff <= yoff1; yoff += ystep) { + for (var zoff = zoff0; zoff <= zoff1; zoff += zstep) { + var idx = xoff+yoff+zoff; + bins[idx][binLengths[idx]++] = bi; + } + } + } + } + + // Put all bodies into the bins + for(var i=0; i!==N; i++){ + var bi = bodies[i]; + var si = bi.shape; + + switch(si.type){ + case SPHERE: + // Put in bin + // check if overlap with other bins + var x = bi.position.x, + y = bi.position.y, + z = bi.position.z; + var r = si.radius; + + addBoxToBins(x-r, y-r, z-r, x+r, y+r, z+r, bi); + break; + + case PLANE: + if(si.worldNormalNeedsUpdate){ + si.computeWorldNormal(bi.quaternion); + } + var planeNormal = si.worldNormal; + + //Relative position from origin of plane object to the first bin + //Incremented as we iterate through the bins + var xreset = xmin + binsizeX*0.5 - bi.position.x, + yreset = ymin + binsizeY*0.5 - bi.position.y, + zreset = zmin + binsizeZ*0.5 - bi.position.z; + + var d = GridBroadphase_collisionPairs_d; + d.set(xreset, yreset, zreset); + + for (var xi = 0, xoff = 0; xi !== nx; xi++, xoff += xstep, d.y = yreset, d.x += binsizeX) { + for (var yi = 0, yoff = 0; yi !== ny; yi++, yoff += ystep, d.z = zreset, d.y += binsizeY) { + for (var zi = 0, zoff = 0; zi !== nz; zi++, zoff += zstep, d.z += binsizeZ) { + if (d.dot(planeNormal) < binRadius) { + var idx = xoff + yoff + zoff; + bins[idx][binLengths[idx]++] = bi; + } + } + } + } + break; + + default: + if (bi.aabbNeedsUpdate) { + bi.computeAABB(); + } + + addBoxToBins( + bi.aabb.lowerBound.x, + bi.aabb.lowerBound.y, + bi.aabb.lowerBound.z, + bi.aabb.upperBound.x, + bi.aabb.upperBound.y, + bi.aabb.upperBound.z, + bi); + break; + } + } + + // Check each bin + for(var i=0; i!==Nbins; i++){ + var binLength = binLengths[i]; + //Skip bins with no potential collisions + if (binLength > 1) { + var bin = bins[i]; + + // Do N^2 broadphase inside + for(var xi=0; xi!==binLength; xi++){ + var bi = bin[xi]; + for(var yi=0; yi!==xi; yi++){ + var bj = bin[yi]; + if(this.needBroadphaseCollision(bi,bj)){ + this.intersectionTest(bi,bj,pairs1,pairs2); + } + } + } + } + } + +// for (var zi = 0, zoff=0; zi < nz; zi++, zoff+= zstep) { +// console.log("layer "+zi); +// for (var yi = 0, yoff=0; yi < ny; yi++, yoff += ystep) { +// var row = ''; +// for (var xi = 0, xoff=0; xi < nx; xi++, xoff += xstep) { +// var idx = xoff + yoff + zoff; +// row += ' ' + binLengths[idx]; +// } +// console.log(row); +// } +// } + + this.makePairsUnique(pairs1,pairs2); +}; + +},{"../math/Vec3":30,"../shapes/Shape":43,"./Broadphase":5}],7:[function(_dereq_,module,exports){ +module.exports = NaiveBroadphase; + +var Broadphase = _dereq_('./Broadphase'); +var AABB = _dereq_('./AABB'); + +/** + * Naive broadphase implementation, used in lack of better ones. + * @class NaiveBroadphase + * @constructor + * @description The naive broadphase looks at all possible pairs without restriction, therefore it has complexity N^2 (which is bad) + * @extends Broadphase + */ +function NaiveBroadphase(){ + Broadphase.apply(this); +} +NaiveBroadphase.prototype = new Broadphase(); +NaiveBroadphase.prototype.constructor = NaiveBroadphase; + +/** + * Get all the collision pairs in the physics world + * @method collisionPairs + * @param {World} world + * @param {Array} pairs1 + * @param {Array} pairs2 + */ +NaiveBroadphase.prototype.collisionPairs = function(world,pairs1,pairs2){ + var bodies = world.bodies, + n = bodies.length, + i,j,bi,bj; + + // Naive N^2 ftw! + for(i=0; i!==n; i++){ + for(j=0; j!==i; j++){ + + bi = bodies[i]; + bj = bodies[j]; + + if(!this.needBroadphaseCollision(bi,bj)){ + continue; + } + + this.intersectionTest(bi,bj,pairs1,pairs2); + } + } +}; + +var tmpAABB = new AABB(); + +/** + * Returns all the bodies within an AABB. + * @method aabbQuery + * @param {World} world + * @param {AABB} aabb + * @param {array} result An array to store resulting bodies in. + * @return {array} + */ +NaiveBroadphase.prototype.aabbQuery = function(world, aabb, result){ + result = result || []; + + for(var i = 0; i < world.bodies.length; i++){ + var b = world.bodies[i]; + + if(b.aabbNeedsUpdate){ + b.computeAABB(); + } + + // Ugly hack until Body gets aabb + if(b.aabb.overlaps(aabb)){ + result.push(b); + } + } + + return result; +}; +},{"./AABB":3,"./Broadphase":5}],8:[function(_dereq_,module,exports){ +module.exports = ObjectCollisionMatrix; + +/** + * Records what objects are colliding with each other + * @class ObjectCollisionMatrix + * @constructor + */ +function ObjectCollisionMatrix() { + + /** + * The matrix storage + * @property matrix + * @type {Object} + */ + this.matrix = {}; +} + +/** + * @method get + * @param {Number} i + * @param {Number} j + * @return {Number} + */ +ObjectCollisionMatrix.prototype.get = function(i, j) { + i = i.id; + j = j.id; + if (j > i) { + var temp = j; + j = i; + i = temp; + } + return i+'-'+j in this.matrix; +}; + +/** + * @method set + * @param {Number} i + * @param {Number} j + * @param {Number} value + */ +ObjectCollisionMatrix.prototype.set = function(i, j, value) { + i = i.id; + j = j.id; + if (j > i) { + var temp = j; + j = i; + i = temp; + } + if (value) { + this.matrix[i+'-'+j] = true; + } + else { + delete this.matrix[i+'-'+j]; + } +}; + +/** + * Empty the matrix + * @method reset + */ +ObjectCollisionMatrix.prototype.reset = function() { + this.matrix = {}; +}; + +/** + * Set max number of objects + * @method setNumObjects + * @param {Number} n + */ +ObjectCollisionMatrix.prototype.setNumObjects = function(n) { +}; + +},{}],9:[function(_dereq_,module,exports){ +module.exports = Ray; + +var Vec3 = _dereq_('../math/Vec3'); +var Quaternion = _dereq_('../math/Quaternion'); +var Transform = _dereq_('../math/Transform'); +var ConvexPolyhedron = _dereq_('../shapes/ConvexPolyhedron'); +var Box = _dereq_('../shapes/Box'); +var RaycastResult = _dereq_('../collision/RaycastResult'); +var Shape = _dereq_('../shapes/Shape'); +var AABB = _dereq_('../collision/AABB'); + +/** + * A line in 3D space that intersects bodies and return points. + * @class Ray + * @constructor + * @param {Vec3} from + * @param {Vec3} to + */ +function Ray(from, to){ + /** + * @property {Vec3} from + */ + this.from = from ? from.clone() : new Vec3(); + + /** + * @property {Vec3} to + */ + this.to = to ? to.clone() : new Vec3(); + + /** + * @private + * @property {Vec3} _direction + */ + this._direction = new Vec3(); + + /** + * The precision of the ray. Used when checking parallelity etc. + * @property {Number} precision + */ + this.precision = 0.0001; + + /** + * Set to true if you want the Ray to take .collisionResponse flags into account on bodies and shapes. + * @property {Boolean} checkCollisionResponse + */ + this.checkCollisionResponse = true; + + /** + * If set to true, the ray skips any hits with normal.dot(rayDirection) < 0. + * @property {Boolean} skipBackfaces + */ + this.skipBackfaces = false; + + /** + * @property {number} collisionFilterMask + * @default -1 + */ + this.collisionFilterMask = -1; + + /** + * @property {number} collisionFilterGroup + * @default -1 + */ + this.collisionFilterGroup = -1; + + /** + * The intersection mode. Should be Ray.ANY, Ray.ALL or Ray.CLOSEST. + * @property {number} mode + */ + this.mode = Ray.ANY; + + /** + * Current result object. + * @property {RaycastResult} result + */ + this.result = new RaycastResult(); + + /** + * Will be set to true during intersectWorld() if the ray hit anything. + * @property {Boolean} hasHit + */ + this.hasHit = false; + + /** + * Current, user-provided result callback. Will be used if mode is Ray.ALL. + * @property {Function} callback + */ + this.callback = function(result){}; +} +Ray.prototype.constructor = Ray; + +Ray.CLOSEST = 1; +Ray.ANY = 2; +Ray.ALL = 4; + +var tmpAABB = new AABB(); +var tmpArray = []; + +/** + * Do itersection against all bodies in the given World. + * @method intersectWorld + * @param {World} world + * @param {object} options + * @return {Boolean} True if the ray hit anything, otherwise false. + */ +Ray.prototype.intersectWorld = function (world, options) { + this.mode = options.mode || Ray.ANY; + this.result = options.result || new RaycastResult(); + this.skipBackfaces = !!options.skipBackfaces; + this.collisionFilterMask = typeof(options.collisionFilterMask) !== 'undefined' ? options.collisionFilterMask : -1; + this.collisionFilterGroup = typeof(options.collisionFilterGroup) !== 'undefined' ? options.collisionFilterGroup : -1; + if(options.from){ + this.from.copy(options.from); + } + if(options.to){ + this.to.copy(options.to); + } + this.callback = options.callback || function(){}; + this.hasHit = false; + + this.result.reset(); + this._updateDirection(); + + this.getAABB(tmpAABB); + tmpArray.length = 0; + world.broadphase.aabbQuery(world, tmpAABB, tmpArray); + this.intersectBodies(tmpArray); + + return this.hasHit; +}; + +var v1 = new Vec3(), + v2 = new Vec3(); + +/* + * As per "Barycentric Technique" as named here http://www.blackpawn.com/texts/pointinpoly/default.html But without the division + */ +Ray.pointInTriangle = pointInTriangle; +function pointInTriangle(p, a, b, c) { + c.vsub(a,v0); + b.vsub(a,v1); + p.vsub(a,v2); + + var dot00 = v0.dot( v0 ); + var dot01 = v0.dot( v1 ); + var dot02 = v0.dot( v2 ); + var dot11 = v1.dot( v1 ); + var dot12 = v1.dot( v2 ); + + var u,v; + + return ( (u = dot11 * dot02 - dot01 * dot12) >= 0 ) && + ( (v = dot00 * dot12 - dot01 * dot02) >= 0 ) && + ( u + v < ( dot00 * dot11 - dot01 * dot01 ) ); +} + +/** + * Shoot a ray at a body, get back information about the hit. + * @method intersectBody + * @private + * @param {Body} body + * @param {RaycastResult} [result] Deprecated - set the result property of the Ray instead. + */ +var intersectBody_xi = new Vec3(); +var intersectBody_qi = new Quaternion(); +Ray.prototype.intersectBody = function (body, result) { + if(result){ + this.result = result; + this._updateDirection(); + } + var checkCollisionResponse = this.checkCollisionResponse; + + if(checkCollisionResponse && !body.collisionResponse){ + return; + } + + if((this.collisionFilterGroup & body.collisionFilterMask)===0 || (body.collisionFilterGroup & this.collisionFilterMask)===0){ + return; + } + + var xi = intersectBody_xi; + var qi = intersectBody_qi; + + for (var i = 0, N = body.shapes.length; i < N; i++) { + var shape = body.shapes[i]; + + if(checkCollisionResponse && !shape.collisionResponse){ + continue; // Skip + } + + body.quaternion.mult(body.shapeOrientations[i], qi); + body.quaternion.vmult(body.shapeOffsets[i], xi); + xi.vadd(body.position, xi); + + this.intersectShape( + shape, + qi, + xi, + body + ); + + if(this.result._shouldStop){ + break; + } + } +}; + +/** + * @method intersectBodies + * @param {Array} bodies An array of Body objects. + * @param {RaycastResult} [result] Deprecated + */ +Ray.prototype.intersectBodies = function (bodies, result) { + if(result){ + this.result = result; + this._updateDirection(); + } + + for ( var i = 0, l = bodies.length; !this.result._shouldStop && i < l; i ++ ) { + this.intersectBody(bodies[i]); + } +}; + +/** + * Updates the _direction vector. + * @private + * @method _updateDirection + */ +Ray.prototype._updateDirection = function(){ + this.to.vsub(this.from, this._direction); + this._direction.normalize(); +}; + +/** + * @method intersectShape + * @private + * @param {Shape} shape + * @param {Quaternion} quat + * @param {Vec3} position + * @param {Body} body + */ +Ray.prototype.intersectShape = function(shape, quat, position, body){ + var from = this.from; + + + // Checking boundingSphere + var distance = distanceFromIntersection(from, this._direction, position); + if ( distance > shape.boundingSphereRadius ) { + return; + } + + var intersectMethod = this[shape.type]; + if(intersectMethod){ + intersectMethod.call(this, shape, quat, position, body); + } +}; + +var vector = new Vec3(); +var normal = new Vec3(); +var intersectPoint = new Vec3(); + +var a = new Vec3(); +var b = new Vec3(); +var c = new Vec3(); +var d = new Vec3(); + +var tmpRaycastResult = new RaycastResult(); + +/** + * @method intersectBox + * @private + * @param {Shape} shape + * @param {Quaternion} quat + * @param {Vec3} position + * @param {Body} body + */ +Ray.prototype.intersectBox = function(shape, quat, position, body){ + return this.intersectConvex(shape.convexPolyhedronRepresentation, quat, position, body); +}; +Ray.prototype[Shape.types.BOX] = Ray.prototype.intersectBox; + +/** + * @method intersectPlane + * @private + * @param {Shape} shape + * @param {Quaternion} quat + * @param {Vec3} position + * @param {Body} body + */ +Ray.prototype.intersectPlane = function(shape, quat, position, body){ + var from = this.from; + var to = this.to; + var direction = this._direction; + + // Get plane normal + var worldNormal = new Vec3(0, 0, 1); + quat.vmult(worldNormal, worldNormal); + + var len = new Vec3(); + from.vsub(position, len); + var planeToFrom = len.dot(worldNormal); + to.vsub(position, len); + var planeToTo = len.dot(worldNormal); + + if(planeToFrom * planeToTo > 0){ + // "from" and "to" are on the same side of the plane... bail out + return; + } + + if(from.distanceTo(to) < planeToFrom){ + return; + } + + var n_dot_dir = worldNormal.dot(direction); + + if (Math.abs(n_dot_dir) < this.precision) { + // No intersection + return; + } + + var planePointToFrom = new Vec3(); + var dir_scaled_with_t = new Vec3(); + var hitPointWorld = new Vec3(); + + from.vsub(position, planePointToFrom); + var t = -worldNormal.dot(planePointToFrom) / n_dot_dir; + direction.scale(t, dir_scaled_with_t); + from.vadd(dir_scaled_with_t, hitPointWorld); + + this.reportIntersection(worldNormal, hitPointWorld, shape, body, -1); +}; +Ray.prototype[Shape.types.PLANE] = Ray.prototype.intersectPlane; + +/** + * Get the world AABB of the ray. + * @method getAABB + * @param {AABB} aabb + */ +Ray.prototype.getAABB = function(result){ + var to = this.to; + var from = this.from; + result.lowerBound.x = Math.min(to.x, from.x); + result.lowerBound.y = Math.min(to.y, from.y); + result.lowerBound.z = Math.min(to.z, from.z); + result.upperBound.x = Math.max(to.x, from.x); + result.upperBound.y = Math.max(to.y, from.y); + result.upperBound.z = Math.max(to.z, from.z); +}; + +var intersectConvexOptions = { + faceList: [0] +}; + +/** + * @method intersectHeightfield + * @private + * @param {Shape} shape + * @param {Quaternion} quat + * @param {Vec3} position + * @param {Body} body + */ +Ray.prototype.intersectHeightfield = function(shape, quat, position, body){ + var data = shape.data, + w = shape.elementSize, + worldPillarOffset = new Vec3(); + + // Convert the ray to local heightfield coordinates + var localRay = new Ray(this.from, this.to); + Transform.pointToLocalFrame(position, quat, localRay.from, localRay.from); + Transform.pointToLocalFrame(position, quat, localRay.to, localRay.to); + + // Get the index of the data points to test against + var index = []; + var iMinX = null; + var iMinY = null; + var iMaxX = null; + var iMaxY = null; + + var inside = shape.getIndexOfPosition(localRay.from.x, localRay.from.y, index, false); + if(inside){ + iMinX = index[0]; + iMinY = index[1]; + iMaxX = index[0]; + iMaxY = index[1]; + } + inside = shape.getIndexOfPosition(localRay.to.x, localRay.to.y, index, false); + if(inside){ + if (iMinX === null || index[0] < iMinX) { iMinX = index[0]; } + if (iMaxX === null || index[0] > iMaxX) { iMaxX = index[0]; } + if (iMinY === null || index[1] < iMinY) { iMinY = index[1]; } + if (iMaxY === null || index[1] > iMaxY) { iMaxY = index[1]; } + } + + if(iMinX === null){ + return; + } + + var minMax = []; + shape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax); + var min = minMax[0]; + var max = minMax[1]; + + // // Bail out if the ray can't touch the bounding box + // // TODO + // var aabb = new AABB(); + // this.getAABB(aabb); + // if(aabb.intersects()){ + // return; + // } + + for(var i = iMinX; i <= iMaxX; i++){ + for(var j = iMinY; j <= iMaxY; j++){ + + if(this.result._shouldStop){ + return; + } + + // Lower triangle + shape.getConvexTrianglePillar(i, j, false); + Transform.pointToWorldFrame(position, quat, shape.pillarOffset, worldPillarOffset); + this.intersectConvex(shape.pillarConvex, quat, worldPillarOffset, body, intersectConvexOptions); + + if(this.result._shouldStop){ + return; + } + + // Upper triangle + shape.getConvexTrianglePillar(i, j, true); + Transform.pointToWorldFrame(position, quat, shape.pillarOffset, worldPillarOffset); + this.intersectConvex(shape.pillarConvex, quat, worldPillarOffset, body, intersectConvexOptions); + } + } +}; +Ray.prototype[Shape.types.HEIGHTFIELD] = Ray.prototype.intersectHeightfield; + +var Ray_intersectSphere_intersectionPoint = new Vec3(); +var Ray_intersectSphere_normal = new Vec3(); + +/** + * @method intersectSphere + * @private + * @param {Shape} shape + * @param {Quaternion} quat + * @param {Vec3} position + * @param {Body} body + */ +Ray.prototype.intersectSphere = function(shape, quat, position, body){ + var from = this.from, + to = this.to, + r = shape.radius; + + var a = Math.pow(to.x - from.x, 2) + Math.pow(to.y - from.y, 2) + Math.pow(to.z - from.z, 2); + var b = 2 * ((to.x - from.x) * (from.x - position.x) + (to.y - from.y) * (from.y - position.y) + (to.z - from.z) * (from.z - position.z)); + var c = Math.pow(from.x - position.x, 2) + Math.pow(from.y - position.y, 2) + Math.pow(from.z - position.z, 2) - Math.pow(r, 2); + + var delta = Math.pow(b, 2) - 4 * a * c; + + var intersectionPoint = Ray_intersectSphere_intersectionPoint; + var normal = Ray_intersectSphere_normal; + + if(delta < 0){ + // No intersection + return; + + } else if(delta === 0){ + // single intersection point + from.lerp(to, delta, intersectionPoint); + + intersectionPoint.vsub(position, normal); + normal.normalize(); + + this.reportIntersection(normal, intersectionPoint, shape, body, -1); + + } else { + var d1 = (- b - Math.sqrt(delta)) / (2 * a); + var d2 = (- b + Math.sqrt(delta)) / (2 * a); + + if(d1 >= 0 && d1 <= 1){ + from.lerp(to, d1, intersectionPoint); + intersectionPoint.vsub(position, normal); + normal.normalize(); + this.reportIntersection(normal, intersectionPoint, shape, body, -1); + } + + if(this.result._shouldStop){ + return; + } + + if(d2 >= 0 && d2 <= 1){ + from.lerp(to, d2, intersectionPoint); + intersectionPoint.vsub(position, normal); + normal.normalize(); + this.reportIntersection(normal, intersectionPoint, shape, body, -1); + } + } +}; +Ray.prototype[Shape.types.SPHERE] = Ray.prototype.intersectSphere; + + +var intersectConvex_normal = new Vec3(); +var intersectConvex_minDistNormal = new Vec3(); +var intersectConvex_minDistIntersect = new Vec3(); +var intersectConvex_vector = new Vec3(); + +/** + * @method intersectConvex + * @private + * @param {Shape} shape + * @param {Quaternion} quat + * @param {Vec3} position + * @param {Body} body + * @param {object} [options] + * @param {array} [options.faceList] + */ +Ray.prototype.intersectConvex = function intersectConvex( + shape, + quat, + position, + body, + options +){ + var minDistNormal = intersectConvex_minDistNormal; + var normal = intersectConvex_normal; + var vector = intersectConvex_vector; + var minDistIntersect = intersectConvex_minDistIntersect; + var faceList = (options && options.faceList) || null; + + // Checking faces + var faces = shape.faces, + vertices = shape.vertices, + normals = shape.faceNormals; + var direction = this._direction; + + var from = this.from; + var to = this.to; + var fromToDistance = from.distanceTo(to); + + var minDist = -1; + var Nfaces = faceList ? faceList.length : faces.length; + var result = this.result; + + for (var j = 0; !result._shouldStop && j < Nfaces; j++) { + var fi = faceList ? faceList[j] : j; + + var face = faces[fi]; + var faceNormal = normals[fi]; + var q = quat; + var x = position; + + // determine if ray intersects the plane of the face + // note: this works regardless of the direction of the face normal + + // Get plane point in world coordinates... + vector.copy(vertices[face[0]]); + q.vmult(vector,vector); + vector.vadd(x,vector); + + // ...but make it relative to the ray from. We'll fix this later. + vector.vsub(from,vector); + + // Get plane normal + q.vmult(faceNormal,normal); + + // If this dot product is negative, we have something interesting + var dot = direction.dot(normal); + + // Bail out if ray and plane are parallel + if ( Math.abs( dot ) < this.precision ){ + continue; + } + + // calc distance to plane + var scalar = normal.dot(vector) / dot; + + // if negative distance, then plane is behind ray + if (scalar < 0){ + continue; + } + + // if (dot < 0) { + + // Intersection point is from + direction * scalar + direction.mult(scalar,intersectPoint); + intersectPoint.vadd(from,intersectPoint); + + // a is the point we compare points b and c with. + a.copy(vertices[face[0]]); + q.vmult(a,a); + x.vadd(a,a); + + for(var i = 1; !result._shouldStop && i < face.length - 1; i++){ + // Transform 3 vertices to world coords + b.copy(vertices[face[i]]); + c.copy(vertices[face[i+1]]); + q.vmult(b,b); + q.vmult(c,c); + x.vadd(b,b); + x.vadd(c,c); + + var distance = intersectPoint.distanceTo(from); + + if(!(pointInTriangle(intersectPoint, a, b, c) || pointInTriangle(intersectPoint, b, a, c)) || distance > fromToDistance){ + continue; + } + + this.reportIntersection(normal, intersectPoint, shape, body, fi); + } + // } + } +}; +Ray.prototype[Shape.types.CONVEXPOLYHEDRON] = Ray.prototype.intersectConvex; + +var intersectTrimesh_normal = new Vec3(); +var intersectTrimesh_localDirection = new Vec3(); +var intersectTrimesh_localFrom = new Vec3(); +var intersectTrimesh_localTo = new Vec3(); +var intersectTrimesh_worldNormal = new Vec3(); +var intersectTrimesh_worldIntersectPoint = new Vec3(); +var intersectTrimesh_localAABB = new AABB(); +var intersectTrimesh_triangles = []; +var intersectTrimesh_treeTransform = new Transform(); + +/** + * @method intersectTrimesh + * @private + * @param {Shape} shape + * @param {Quaternion} quat + * @param {Vec3} position + * @param {Body} body + * @param {object} [options] + * @todo Optimize by transforming the world to local space first. + * @todo Use Octree lookup + */ +Ray.prototype.intersectTrimesh = function intersectTrimesh( + mesh, + quat, + position, + body, + options +){ + var normal = intersectTrimesh_normal; + var triangles = intersectTrimesh_triangles; + var treeTransform = intersectTrimesh_treeTransform; + var minDistNormal = intersectConvex_minDistNormal; + var vector = intersectConvex_vector; + var minDistIntersect = intersectConvex_minDistIntersect; + var localAABB = intersectTrimesh_localAABB; + var localDirection = intersectTrimesh_localDirection; + var localFrom = intersectTrimesh_localFrom; + var localTo = intersectTrimesh_localTo; + var worldIntersectPoint = intersectTrimesh_worldIntersectPoint; + var worldNormal = intersectTrimesh_worldNormal; + var faceList = (options && options.faceList) || null; + + // Checking faces + var indices = mesh.indices, + vertices = mesh.vertices, + normals = mesh.faceNormals; + + var from = this.from; + var to = this.to; + var direction = this._direction; + + var minDist = -1; + treeTransform.position.copy(position); + treeTransform.quaternion.copy(quat); + + // Transform ray to local space! + Transform.vectorToLocalFrame(position, quat, direction, localDirection); + //body.vectorToLocalFrame(direction, localDirection); + Transform.pointToLocalFrame(position, quat, from, localFrom); + //body.pointToLocalFrame(from, localFrom); + Transform.pointToLocalFrame(position, quat, to, localTo); + //body.pointToLocalFrame(to, localTo); + var fromToDistanceSquared = localFrom.distanceSquared(localTo); + + mesh.tree.rayQuery(this, treeTransform, triangles); + + for (var i = 0, N = triangles.length; !this.result._shouldStop && i !== N; i++) { + var trianglesIndex = triangles[i]; + + mesh.getNormal(trianglesIndex, normal); + + // determine if ray intersects the plane of the face + // note: this works regardless of the direction of the face normal + + // Get plane point in world coordinates... + mesh.getVertex(indices[trianglesIndex * 3], a); + + // ...but make it relative to the ray from. We'll fix this later. + a.vsub(localFrom,vector); + + // Get plane normal + // quat.vmult(normal, normal); + + // If this dot product is negative, we have something interesting + var dot = localDirection.dot(normal); + + // Bail out if ray and plane are parallel + // if (Math.abs( dot ) < this.precision){ + // continue; + // } + + // calc distance to plane + var scalar = normal.dot(vector) / dot; + + // if negative distance, then plane is behind ray + if (scalar < 0){ + continue; + } + + // Intersection point is from + direction * scalar + localDirection.scale(scalar,intersectPoint); + intersectPoint.vadd(localFrom,intersectPoint); + + // Get triangle vertices + mesh.getVertex(indices[trianglesIndex * 3 + 1], b); + mesh.getVertex(indices[trianglesIndex * 3 + 2], c); + + var squaredDistance = intersectPoint.distanceSquared(localFrom); + + if(!(pointInTriangle(intersectPoint, b, a, c) || pointInTriangle(intersectPoint, a, b, c)) || squaredDistance > fromToDistanceSquared){ + continue; + } + + // transform intersectpoint and normal to world + Transform.vectorToWorldFrame(quat, normal, worldNormal); + //body.vectorToWorldFrame(normal, worldNormal); + Transform.pointToWorldFrame(position, quat, intersectPoint, worldIntersectPoint); + //body.pointToWorldFrame(intersectPoint, worldIntersectPoint); + this.reportIntersection(worldNormal, worldIntersectPoint, mesh, body, trianglesIndex); + } + triangles.length = 0; +}; +Ray.prototype[Shape.types.TRIMESH] = Ray.prototype.intersectTrimesh; + + +/** + * @method reportIntersection + * @private + * @param {Vec3} normal + * @param {Vec3} hitPointWorld + * @param {Shape} shape + * @param {Body} body + * @return {boolean} True if the intersections should continue + */ +Ray.prototype.reportIntersection = function(normal, hitPointWorld, shape, body, hitFaceIndex){ + var from = this.from; + var to = this.to; + var distance = from.distanceTo(hitPointWorld); + var result = this.result; + + // Skip back faces? + if(this.skipBackfaces && normal.dot(this._direction) > 0){ + return; + } + + result.hitFaceIndex = typeof(hitFaceIndex) !== 'undefined' ? hitFaceIndex : -1; + + switch(this.mode){ + case Ray.ALL: + this.hasHit = true; + result.set( + from, + to, + normal, + hitPointWorld, + shape, + body, + distance + ); + result.hasHit = true; + this.callback(result); + break; + + case Ray.CLOSEST: + + // Store if closer than current closest + if(distance < result.distance || !result.hasHit){ + this.hasHit = true; + result.hasHit = true; + result.set( + from, + to, + normal, + hitPointWorld, + shape, + body, + distance + ); + } + break; + + case Ray.ANY: + + // Report and stop. + this.hasHit = true; + result.hasHit = true; + result.set( + from, + to, + normal, + hitPointWorld, + shape, + body, + distance + ); + result._shouldStop = true; + break; + } +}; + +var v0 = new Vec3(), + intersect = new Vec3(); +function distanceFromIntersection(from, direction, position) { + + // v0 is vector from from to position + position.vsub(from,v0); + var dot = v0.dot(direction); + + // intersect = direction*dot + from + direction.mult(dot,intersect); + intersect.vadd(from,intersect); + + var distance = position.distanceTo(intersect); + + return distance; +} + + +},{"../collision/AABB":3,"../collision/RaycastResult":10,"../math/Quaternion":28,"../math/Transform":29,"../math/Vec3":30,"../shapes/Box":37,"../shapes/ConvexPolyhedron":38,"../shapes/Shape":43}],10:[function(_dereq_,module,exports){ +var Vec3 = _dereq_('../math/Vec3'); + +module.exports = RaycastResult; + +/** + * Storage for Ray casting data. + * @class RaycastResult + * @constructor + */ +function RaycastResult(){ + + /** + * @property {Vec3} rayFromWorld + */ + this.rayFromWorld = new Vec3(); + + /** + * @property {Vec3} rayToWorld + */ + this.rayToWorld = new Vec3(); + + /** + * @property {Vec3} hitNormalWorld + */ + this.hitNormalWorld = new Vec3(); + + /** + * @property {Vec3} hitPointWorld + */ + this.hitPointWorld = new Vec3(); + + /** + * @property {boolean} hasHit + */ + this.hasHit = false; + + /** + * The hit shape, or null. + * @property {Shape} shape + */ + this.shape = null; + + /** + * The hit body, or null. + * @property {Body} body + */ + this.body = null; + + /** + * The index of the hit triangle, if the hit shape was a trimesh. + * @property {number} hitFaceIndex + * @default -1 + */ + this.hitFaceIndex = -1; + + /** + * Distance to the hit. Will be set to -1 if there was no hit. + * @property {number} distance + * @default -1 + */ + this.distance = -1; + + /** + * If the ray should stop traversing the bodies. + * @private + * @property {Boolean} _shouldStop + * @default false + */ + this._shouldStop = false; +} + +/** + * Reset all result data. + * @method reset + */ +RaycastResult.prototype.reset = function () { + this.rayFromWorld.setZero(); + this.rayToWorld.setZero(); + this.hitNormalWorld.setZero(); + this.hitPointWorld.setZero(); + this.hasHit = false; + this.shape = null; + this.body = null; + this.hitFaceIndex = -1; + this.distance = -1; + this._shouldStop = false; +}; + +/** + * @method abort + */ +RaycastResult.prototype.abort = function(){ + this._shouldStop = true; +}; + +/** + * @method set + * @param {Vec3} rayFromWorld + * @param {Vec3} rayToWorld + * @param {Vec3} hitNormalWorld + * @param {Vec3} hitPointWorld + * @param {Shape} shape + * @param {Body} body + * @param {number} distance + */ +RaycastResult.prototype.set = function( + rayFromWorld, + rayToWorld, + hitNormalWorld, + hitPointWorld, + shape, + body, + distance +){ + this.rayFromWorld.copy(rayFromWorld); + this.rayToWorld.copy(rayToWorld); + this.hitNormalWorld.copy(hitNormalWorld); + this.hitPointWorld.copy(hitPointWorld); + this.shape = shape; + this.body = body; + this.distance = distance; +}; +},{"../math/Vec3":30}],11:[function(_dereq_,module,exports){ +var Shape = _dereq_('../shapes/Shape'); +var Broadphase = _dereq_('../collision/Broadphase'); + +module.exports = SAPBroadphase; + +/** + * Sweep and prune broadphase along one axis. + * + * @class SAPBroadphase + * @constructor + * @param {World} [world] + * @extends Broadphase + */ +function SAPBroadphase(world){ + Broadphase.apply(this); + + /** + * List of bodies currently in the broadphase. + * @property axisList + * @type {Array} + */ + this.axisList = []; + + /** + * The world to search in. + * @property world + * @type {World} + */ + this.world = null; + + /** + * Axis to sort the bodies along. Set to 0 for x axis, and 1 for y axis. For best performance, choose an axis that the bodies are spread out more on. + * @property axisIndex + * @type {Number} + */ + this.axisIndex = 0; + + var axisList = this.axisList; + + this._addBodyHandler = function(e){ + axisList.push(e.body); + }; + + this._removeBodyHandler = function(e){ + var idx = axisList.indexOf(e.body); + if(idx !== -1){ + axisList.splice(idx,1); + } + }; + + if(world){ + this.setWorld(world); + } +} +SAPBroadphase.prototype = new Broadphase(); + +/** + * Change the world + * @method setWorld + * @param {World} world + */ +SAPBroadphase.prototype.setWorld = function(world){ + // Clear the old axis array + this.axisList.length = 0; + + // Add all bodies from the new world + for(var i=0; i=0;j--) { + if(a[j].aabb.lowerBound.x <= v.aabb.lowerBound.x){ + break; + } + a[j+1] = a[j]; + } + a[j+1] = v; + } + return a; +}; + +/** + * @static + * @method insertionSortY + * @param {Array} a + * @return {Array} + */ +SAPBroadphase.insertionSortY = function(a) { + for(var i=1,l=a.length;i=0;j--) { + if(a[j].aabb.lowerBound.y <= v.aabb.lowerBound.y){ + break; + } + a[j+1] = a[j]; + } + a[j+1] = v; + } + return a; +}; + +/** + * @static + * @method insertionSortZ + * @param {Array} a + * @return {Array} + */ +SAPBroadphase.insertionSortZ = function(a) { + for(var i=1,l=a.length;i=0;j--) { + if(a[j].aabb.lowerBound.z <= v.aabb.lowerBound.z){ + break; + } + a[j+1] = a[j]; + } + a[j+1] = v; + } + return a; +}; + +/** + * Collect all collision pairs + * @method collisionPairs + * @param {World} world + * @param {Array} p1 + * @param {Array} p2 + */ +SAPBroadphase.prototype.collisionPairs = function(world,p1,p2){ + var bodies = this.axisList, + N = bodies.length, + axisIndex = this.axisIndex, + i, j; + + if(this.dirty){ + this.sortList(); + this.dirty = false; + } + + // Look through the list + for(i=0; i !== N; i++){ + var bi = bodies[i]; + + for(j=i+1; j < N; j++){ + var bj = bodies[j]; + + if(!this.needBroadphaseCollision(bi,bj)){ + continue; + } + + if(!SAPBroadphase.checkBounds(bi,bj,axisIndex)){ + break; + } + + this.intersectionTest(bi,bj,p1,p2); + } + } +}; + +SAPBroadphase.prototype.sortList = function(){ + var axisList = this.axisList; + var axisIndex = this.axisIndex; + var N = axisList.length; + + // Update AABBs + for(var i = 0; i!==N; i++){ + var bi = axisList[i]; + if(bi.aabbNeedsUpdate){ + bi.computeAABB(); + } + } + + // Sort the list + if(axisIndex === 0){ + SAPBroadphase.insertionSortX(axisList); + } else if(axisIndex === 1){ + SAPBroadphase.insertionSortY(axisList); + } else if(axisIndex === 2){ + SAPBroadphase.insertionSortZ(axisList); + } +}; + +/** + * Check if the bounds of two bodies overlap, along the given SAP axis. + * @static + * @method checkBounds + * @param {Body} bi + * @param {Body} bj + * @param {Number} axisIndex + * @return {Boolean} + */ +SAPBroadphase.checkBounds = function(bi, bj, axisIndex){ + var biPos; + var bjPos; + + if(axisIndex === 0){ + biPos = bi.position.x; + bjPos = bj.position.x; + } else if(axisIndex === 1){ + biPos = bi.position.y; + bjPos = bj.position.y; + } else if(axisIndex === 2){ + biPos = bi.position.z; + bjPos = bj.position.z; + } + + var ri = bi.boundingRadius, + rj = bj.boundingRadius, + boundA1 = biPos - ri, + boundA2 = biPos + ri, + boundB1 = bjPos - rj, + boundB2 = bjPos + rj; + + return boundB1 < boundA2; +}; + +/** + * Computes the variance of the body positions and estimates the best + * axis to use. Will automatically set property .axisIndex. + * @method autoDetectAxis + */ +SAPBroadphase.prototype.autoDetectAxis = function(){ + var sumX=0, + sumX2=0, + sumY=0, + sumY2=0, + sumZ=0, + sumZ2=0, + bodies = this.axisList, + N = bodies.length, + invN=1/N; + + for(var i=0; i!==N; i++){ + var b = bodies[i]; + + var centerX = b.position.x; + sumX += centerX; + sumX2 += centerX*centerX; + + var centerY = b.position.y; + sumY += centerY; + sumY2 += centerY*centerY; + + var centerZ = b.position.z; + sumZ += centerZ; + sumZ2 += centerZ*centerZ; + } + + var varianceX = sumX2 - sumX*sumX*invN, + varianceY = sumY2 - sumY*sumY*invN, + varianceZ = sumZ2 - sumZ*sumZ*invN; + + if(varianceX > varianceY){ + if(varianceX > varianceZ){ + this.axisIndex = 0; + } else{ + this.axisIndex = 2; + } + } else if(varianceY > varianceZ){ + this.axisIndex = 1; + } else{ + this.axisIndex = 2; + } +}; + +/** + * Returns all the bodies within an AABB. + * @method aabbQuery + * @param {World} world + * @param {AABB} aabb + * @param {array} result An array to store resulting bodies in. + * @return {array} + */ +SAPBroadphase.prototype.aabbQuery = function(world, aabb, result){ + result = result || []; + + if(this.dirty){ + this.sortList(); + this.dirty = false; + } + + var axisIndex = this.axisIndex, axis = 'x'; + if(axisIndex === 1){ axis = 'y'; } + if(axisIndex === 2){ axis = 'z'; } + + var axisList = this.axisList; + var lower = aabb.lowerBound[axis]; + var upper = aabb.upperBound[axis]; + for(var i = 0; i < axisList.length; i++){ + var b = axisList[i]; + + if(b.aabbNeedsUpdate){ + b.computeAABB(); + } + + if(b.aabb.overlaps(aabb)){ + result.push(b); + } + } + + return result; +}; +},{"../collision/Broadphase":5,"../shapes/Shape":43}],12:[function(_dereq_,module,exports){ +module.exports = ConeTwistConstraint; + +var Constraint = _dereq_('./Constraint'); +var PointToPointConstraint = _dereq_('./PointToPointConstraint'); +var ConeEquation = _dereq_('../equations/ConeEquation'); +var RotationalEquation = _dereq_('../equations/RotationalEquation'); +var ContactEquation = _dereq_('../equations/ContactEquation'); +var Vec3 = _dereq_('../math/Vec3'); + +/** + * @class ConeTwistConstraint + * @constructor + * @author schteppe + * @param {Body} bodyA + * @param {Body} bodyB + * @param {object} [options] + * @param {Vec3} [options.pivotA] + * @param {Vec3} [options.pivotB] + * @param {Vec3} [options.axisA] + * @param {Vec3} [options.axisB] + * @param {Number} [options.maxForce=1e6] + * @extends PointToPointConstraint + */ +function ConeTwistConstraint(bodyA, bodyB, options){ + options = options || {}; + var maxForce = typeof(options.maxForce) !== 'undefined' ? options.maxForce : 1e6; + + // Set pivot point in between + var pivotA = options.pivotA ? options.pivotA.clone() : new Vec3(); + var pivotB = options.pivotB ? options.pivotB.clone() : new Vec3(); + this.axisA = options.axisA ? options.axisA.clone() : new Vec3(); + this.axisB = options.axisB ? options.axisB.clone() : new Vec3(); + + PointToPointConstraint.call(this, bodyA, pivotA, bodyB, pivotB, maxForce); + + this.collideConnected = !!options.collideConnected; + + this.angle = typeof(options.angle) !== 'undefined' ? options.angle : 0; + + /** + * @property {ConeEquation} coneEquation + */ + var c = this.coneEquation = new ConeEquation(bodyA,bodyB,options); + + /** + * @property {RotationalEquation} twistEquation + */ + var t = this.twistEquation = new RotationalEquation(bodyA,bodyB,options); + this.twistAngle = typeof(options.twistAngle) !== 'undefined' ? options.twistAngle : 0; + + // Make the cone equation push the bodies toward the cone axis, not outward + c.maxForce = 0; + c.minForce = -maxForce; + + // Make the twist equation add torque toward the initial position + t.maxForce = 0; + t.minForce = -maxForce; + + this.equations.push(c, t); +} +ConeTwistConstraint.prototype = new PointToPointConstraint(); +ConeTwistConstraint.constructor = ConeTwistConstraint; + +var ConeTwistConstraint_update_tmpVec1 = new Vec3(); +var ConeTwistConstraint_update_tmpVec2 = new Vec3(); + +ConeTwistConstraint.prototype.update = function(){ + var bodyA = this.bodyA, + bodyB = this.bodyB, + cone = this.coneEquation, + twist = this.twistEquation; + + PointToPointConstraint.prototype.update.call(this); + + // Update the axes to the cone constraint + bodyA.vectorToWorldFrame(this.axisA, cone.axisA); + bodyB.vectorToWorldFrame(this.axisB, cone.axisB); + + // Update the world axes in the twist constraint + this.axisA.tangents(twist.axisA, twist.axisA); + bodyA.vectorToWorldFrame(twist.axisA, twist.axisA); + + this.axisB.tangents(twist.axisB, twist.axisB); + bodyB.vectorToWorldFrame(twist.axisB, twist.axisB); + + cone.angle = this.angle; + twist.maxAngle = this.twistAngle; +}; + + +},{"../equations/ConeEquation":18,"../equations/ContactEquation":19,"../equations/RotationalEquation":22,"../math/Vec3":30,"./Constraint":13,"./PointToPointConstraint":17}],13:[function(_dereq_,module,exports){ +module.exports = Constraint; + +var Utils = _dereq_('../utils/Utils'); + +/** + * Constraint base class + * @class Constraint + * @author schteppe + * @constructor + * @param {Body} bodyA + * @param {Body} bodyB + * @param {object} [options] + * @param {boolean} [options.collideConnected=true] + * @param {boolean} [options.wakeUpBodies=true] + */ +function Constraint(bodyA, bodyB, options){ + options = Utils.defaults(options,{ + collideConnected : true, + wakeUpBodies : true, + }); + + /** + * Equations to be solved in this constraint + * @property equations + * @type {Array} + */ + this.equations = []; + + /** + * @property {Body} bodyA + */ + this.bodyA = bodyA; + + /** + * @property {Body} bodyB + */ + this.bodyB = bodyB; + + /** + * @property {Number} id + */ + this.id = Constraint.idCounter++; + + /** + * Set to true if you want the bodies to collide when they are connected. + * @property collideConnected + * @type {boolean} + */ + this.collideConnected = options.collideConnected; + + if(options.wakeUpBodies){ + if(bodyA){ + bodyA.wakeUp(); + } + if(bodyB){ + bodyB.wakeUp(); + } + } +} + +/** + * Update all the equations with data. + * @method update + */ +Constraint.prototype.update = function(){ + throw new Error("method update() not implmemented in this Constraint subclass!"); +}; + +/** + * Enables all equations in the constraint. + * @method enable + */ +Constraint.prototype.enable = function(){ + var eqs = this.equations; + for(var i=0; i + // G = [0 axisA 0 -axisB] + + GA.rotational.copy(axisA); + axisB.negate(GB.rotational); + + var GW = this.computeGW() - this.targetVelocity, + GiMf = this.computeGiMf(); + + var B = - GW * b - h * GiMf; + + return B; +}; + +},{"../math/Mat3":27,"../math/Vec3":30,"./Equation":20}],24:[function(_dereq_,module,exports){ +var Utils = _dereq_('../utils/Utils'); + +module.exports = ContactMaterial; + +/** + * Defines what happens when two materials meet. + * @class ContactMaterial + * @constructor + * @param {Material} m1 + * @param {Material} m2 + * @param {object} [options] + * @param {Number} [options.friction=0.3] + * @param {Number} [options.restitution=0.3] + * @param {number} [options.contactEquationStiffness=1e7] + * @param {number} [options.contactEquationRelaxation=3] + * @param {number} [options.frictionEquationStiffness=1e7] + * @param {Number} [options.frictionEquationRelaxation=3] + */ +function ContactMaterial(m1, m2, options){ + options = Utils.defaults(options, { + friction: 0.3, + restitution: 0.3, + contactEquationStiffness: 1e7, + contactEquationRelaxation: 3, + frictionEquationStiffness: 1e7, + frictionEquationRelaxation: 3 + }); + + /** + * Identifier of this material + * @property {Number} id + */ + this.id = ContactMaterial.idCounter++; + + /** + * Participating materials + * @property {Array} materials + * @todo Should be .materialA and .materialB instead + */ + this.materials = [m1, m2]; + + /** + * Friction coefficient + * @property {Number} friction + */ + this.friction = options.friction; + + /** + * Restitution coefficient + * @property {Number} restitution + */ + this.restitution = options.restitution; + + /** + * Stiffness of the produced contact equations + * @property {Number} contactEquationStiffness + */ + this.contactEquationStiffness = options.contactEquationStiffness; + + /** + * Relaxation time of the produced contact equations + * @property {Number} contactEquationRelaxation + */ + this.contactEquationRelaxation = options.contactEquationRelaxation; + + /** + * Stiffness of the produced friction equations + * @property {Number} frictionEquationStiffness + */ + this.frictionEquationStiffness = options.frictionEquationStiffness; + + /** + * Relaxation time of the produced friction equations + * @property {Number} frictionEquationRelaxation + */ + this.frictionEquationRelaxation = options.frictionEquationRelaxation; +} + +ContactMaterial.idCounter = 0; + +},{"../utils/Utils":53}],25:[function(_dereq_,module,exports){ +module.exports = Material; + +/** + * Defines a physics material. + * @class Material + * @constructor + * @param {object} [options] + * @author schteppe + */ +function Material(options){ + var name = ''; + options = options || {}; + + // Backwards compatibility fix + if(typeof(options) === 'string'){ + name = options; + options = {}; + } else if(typeof(options) === 'object') { + name = ''; + } + + /** + * @property name + * @type {String} + */ + this.name = name; + + /** + * material id. + * @property id + * @type {number} + */ + this.id = Material.idCounter++; + + /** + * Friction for this material. If non-negative, it will be used instead of the friction given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used. + * @property {number} friction + */ + this.friction = typeof(options.friction) !== 'undefined' ? options.friction : -1; + + /** + * Restitution for this material. If non-negative, it will be used instead of the restitution given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used. + * @property {number} restitution + */ + this.restitution = typeof(options.restitution) !== 'undefined' ? options.restitution : -1; +} + +Material.idCounter = 0; + +},{}],26:[function(_dereq_,module,exports){ +module.exports = JacobianElement; + +var Vec3 = _dereq_('./Vec3'); + +/** + * An element containing 6 entries, 3 spatial and 3 rotational degrees of freedom. + * @class JacobianElement + * @constructor + */ +function JacobianElement(){ + + /** + * @property {Vec3} spatial + */ + this.spatial = new Vec3(); + + /** + * @property {Vec3} rotational + */ + this.rotational = new Vec3(); +} + +/** + * Multiply with other JacobianElement + * @method multiplyElement + * @param {JacobianElement} element + * @return {Number} + */ +JacobianElement.prototype.multiplyElement = function(element){ + return element.spatial.dot(this.spatial) + element.rotational.dot(this.rotational); +}; + +/** + * Multiply with two vectors + * @method multiplyVectors + * @param {Vec3} spatial + * @param {Vec3} rotational + * @return {Number} + */ +JacobianElement.prototype.multiplyVectors = function(spatial,rotational){ + return spatial.dot(this.spatial) + rotational.dot(this.rotational); +}; + +},{"./Vec3":30}],27:[function(_dereq_,module,exports){ +module.exports = Mat3; + +var Vec3 = _dereq_('./Vec3'); + +/** + * A 3x3 matrix. + * @class Mat3 + * @constructor + * @param array elements Array of nine elements. Optional. + * @author schteppe / http://github.com/schteppe + */ +function Mat3(elements){ + /** + * A vector of length 9, containing all matrix elements + * @property {Array} elements + */ + if(elements){ + this.elements = elements; + } else { + this.elements = [0,0,0,0,0,0,0,0,0]; + } +} + +/** + * Sets the matrix to identity + * @method identity + * @todo Should perhaps be renamed to setIdentity() to be more clear. + * @todo Create another function that immediately creates an identity matrix eg. eye() + */ +Mat3.prototype.identity = function(){ + var e = this.elements; + e[0] = 1; + e[1] = 0; + e[2] = 0; + + e[3] = 0; + e[4] = 1; + e[5] = 0; + + e[6] = 0; + e[7] = 0; + e[8] = 1; +}; + +/** + * Set all elements to zero + * @method setZero + */ +Mat3.prototype.setZero = function(){ + var e = this.elements; + e[0] = 0; + e[1] = 0; + e[2] = 0; + e[3] = 0; + e[4] = 0; + e[5] = 0; + e[6] = 0; + e[7] = 0; + e[8] = 0; +}; + +/** + * Sets the matrix diagonal elements from a Vec3 + * @method setTrace + * @param {Vec3} vec3 + */ +Mat3.prototype.setTrace = function(vec3){ + var e = this.elements; + e[0] = vec3.x; + e[4] = vec3.y; + e[8] = vec3.z; +}; + +/** + * Gets the matrix diagonal elements + * @method getTrace + * @return {Vec3} + */ +Mat3.prototype.getTrace = function(target){ + var target = target || new Vec3(); + var e = this.elements; + target.x = e[0]; + target.y = e[4]; + target.z = e[8]; +}; + +/** + * Matrix-Vector multiplication + * @method vmult + * @param {Vec3} v The vector to multiply with + * @param {Vec3} target Optional, target to save the result in. + */ +Mat3.prototype.vmult = function(v,target){ + target = target || new Vec3(); + + var e = this.elements, + x = v.x, + y = v.y, + z = v.z; + target.x = e[0]*x + e[1]*y + e[2]*z; + target.y = e[3]*x + e[4]*y + e[5]*z; + target.z = e[6]*x + e[7]*y + e[8]*z; + + return target; +}; + +/** + * Matrix-scalar multiplication + * @method smult + * @param {Number} s + */ +Mat3.prototype.smult = function(s){ + for(var i=0; i1 acos and sqrt will produce errors, this cant happen if quaternion is normalised + var angle = 2 * Math.acos(this.w); + var s = Math.sqrt(1-this.w*this.w); // assuming quaternion normalised then w is less than 1, so term always positive. + if (s < 0.001) { // test to avoid divide by zero, s is always positive due to sqrt + // if s close to zero then direction of axis not important + targetAxis.x = this.x; // if it is important that axis is normalised then replace with x=1; y=z=0; + targetAxis.y = this.y; + targetAxis.z = this.z; + } else { + targetAxis.x = this.x / s; // normalise axis + targetAxis.y = this.y / s; + targetAxis.z = this.z / s; + } + return [targetAxis,angle]; +}; + +var sfv_t1 = new Vec3(), + sfv_t2 = new Vec3(); + +/** + * Set the quaternion value given two vectors. The resulting rotation will be the needed rotation to rotate u to v. + * @method setFromVectors + * @param {Vec3} u + * @param {Vec3} v + */ +Quaternion.prototype.setFromVectors = function(u,v){ + if(u.isAntiparallelTo(v)){ + var t1 = sfv_t1; + var t2 = sfv_t2; + + u.tangents(t1,t2); + this.setFromAxisAngle(t1,Math.PI); + } else { + var a = u.cross(v); + this.x = a.x; + this.y = a.y; + this.z = a.z; + this.w = Math.sqrt(Math.pow(u.norm(),2) * Math.pow(v.norm(),2)) + u.dot(v); + this.normalize(); + } +}; + +/** + * Quaternion multiplication + * @method mult + * @param {Quaternion} q + * @param {Quaternion} target Optional. + * @return {Quaternion} + */ +var Quaternion_mult_va = new Vec3(); +var Quaternion_mult_vb = new Vec3(); +var Quaternion_mult_vaxvb = new Vec3(); +Quaternion.prototype.mult = function(q,target){ + target = target || new Quaternion(); + var w = this.w, + va = Quaternion_mult_va, + vb = Quaternion_mult_vb, + vaxvb = Quaternion_mult_vaxvb; + + va.set(this.x,this.y,this.z); + vb.set(q.x,q.y,q.z); + target.w = w*q.w - va.dot(vb); + va.cross(vb,vaxvb); + + target.x = w * vb.x + q.w*va.x + vaxvb.x; + target.y = w * vb.y + q.w*va.y + vaxvb.y; + target.z = w * vb.z + q.w*va.z + vaxvb.z; + + return target; +}; + +/** + * Get the inverse quaternion rotation. + * @method inverse + * @param {Quaternion} target + * @return {Quaternion} + */ +Quaternion.prototype.inverse = function(target){ + var x = this.x, y = this.y, z = this.z, w = this.w; + target = target || new Quaternion(); + + this.conjugate(target); + var inorm2 = 1/(x*x + y*y + z*z + w*w); + target.x *= inorm2; + target.y *= inorm2; + target.z *= inorm2; + target.w *= inorm2; + + return target; +}; + +/** + * Get the quaternion conjugate + * @method conjugate + * @param {Quaternion} target + * @return {Quaternion} + */ +Quaternion.prototype.conjugate = function(target){ + target = target || new Quaternion(); + + target.x = -this.x; + target.y = -this.y; + target.z = -this.z; + target.w = this.w; + + return target; +}; + +/** + * Normalize the quaternion. Note that this changes the values of the quaternion. + * @method normalize + */ +Quaternion.prototype.normalize = function(){ + 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 = 0; + } else { + l = 1 / l; + this.x *= l; + this.y *= l; + this.z *= l; + this.w *= l; + } +}; + +/** + * Approximation of quaternion normalization. Works best when quat is already almost-normalized. + * @method normalizeFast + * @see http://jsperf.com/fast-quaternion-normalization + * @author unphased, https://github.com/unphased + */ +Quaternion.prototype.normalizeFast = function () { + var f = (3.0-(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w))/2.0; + if ( f === 0 ) { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + } else { + this.x *= f; + this.y *= f; + this.z *= f; + this.w *= f; + } +}; + +/** + * Multiply the quaternion by a vector + * @method vmult + * @param {Vec3} v + * @param {Vec3} target Optional + * @return {Vec3} + */ +Quaternion.prototype.vmult = function(v,target){ + target = target || new Vec3(); + + var x = v.x, + y = v.y, + z = v.z; + + var qx = this.x, + qy = this.y, + qz = this.z, + qw = this.w; + + // q*v + var ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + target.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; + target.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; + target.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; + + return target; +}; + +/** + * Copies value of source to this quaternion. + * @method copy + * @param {Quaternion} source + * @return {Quaternion} this + */ +Quaternion.prototype.copy = function(source){ + this.x = source.x; + this.y = source.y; + this.z = source.z; + this.w = source.w; + return this; +}; + +/** + * Convert the quaternion to euler angle representation. Order: YZX, as this page describes: http://www.euclideanspace.com/maths/standards/index.htm + * @method toEuler + * @param {Vec3} target + * @param string order Three-character string e.g. "YZX", which also is default. + */ +Quaternion.prototype.toEuler = function(target,order){ + order = order || "YZX"; + + var heading, attitude, bank; + var x = this.x, y = this.y, z = this.z, w = this.w; + + switch(order){ + case "YZX": + var test = x*y + z*w; + if (test > 0.499) { // singularity at north pole + heading = 2 * Math.atan2(x,w); + attitude = Math.PI/2; + bank = 0; + } + if (test < -0.499) { // singularity at south pole + heading = -2 * Math.atan2(x,w); + attitude = - Math.PI/2; + bank = 0; + } + if(isNaN(heading)){ + var sqx = x*x; + var sqy = y*y; + var sqz = z*z; + heading = Math.atan2(2*y*w - 2*x*z , 1 - 2*sqy - 2*sqz); // Heading + attitude = Math.asin(2*test); // attitude + bank = Math.atan2(2*x*w - 2*y*z , 1 - 2*sqx - 2*sqz); // bank + } + break; + default: + throw new Error("Euler order "+order+" not supported yet."); + } + + target.y = heading; + target.z = attitude; + target.x = bank; +}; + +/** + * See http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m + * @method setFromEuler + * @param {Number} x + * @param {Number} y + * @param {Number} z + * @param {String} order The order to apply angles: 'XYZ' or 'YXZ' or any other combination + */ +Quaternion.prototype.setFromEuler = function ( x, y, z, order ) { + order = order || "XYZ"; + + 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 ); + + 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; + + } + + return this; + +}; + +Quaternion.prototype.clone = function(){ + return new Quaternion(this.x, this.y, this.z, this.w); +}; +},{"./Vec3":30}],29:[function(_dereq_,module,exports){ +var Vec3 = _dereq_('./Vec3'); +var Quaternion = _dereq_('./Quaternion'); + +module.exports = Transform; + +/** + * @class Transform + * @constructor + */ +function Transform(options) { + options = options || {}; + + /** + * @property {Vec3} position + */ + this.position = new Vec3(); + if(options.position){ + this.position.copy(options.position); + } + + /** + * @property {Quaternion} quaternion + */ + this.quaternion = new Quaternion(); + if(options.quaternion){ + this.quaternion.copy(options.quaternion); + } +} + +var tmpQuat = new Quaternion(); + +/** + * @static + * @method pointToLocaFrame + * @param {Vec3} position + * @param {Quaternion} quaternion + * @param {Vec3} worldPoint + * @param {Vec3} result + */ +Transform.pointToLocalFrame = function(position, quaternion, worldPoint, result){ + var result = result || new Vec3(); + worldPoint.vsub(position, result); + quaternion.conjugate(tmpQuat); + tmpQuat.vmult(result, result); + return result; +}; + +/** + * Get a global point in local transform coordinates. + * @method pointToLocal + * @param {Vec3} point + * @param {Vec3} result + * @return {Vec3} The "result" vector object + */ +Transform.prototype.pointToLocal = function(worldPoint, result){ + return Transform.pointToLocalFrame(this.position, this.quaternion, worldPoint, result); +}; + +/** + * @static + * @method pointToWorldFrame + * @param {Vec3} position + * @param {Vec3} quaternion + * @param {Vec3} localPoint + * @param {Vec3} result + */ +Transform.pointToWorldFrame = function(position, quaternion, localPoint, result){ + var result = result || new Vec3(); + quaternion.vmult(localPoint, result); + result.vadd(position, result); + return result; +}; + +/** + * Get a local point in global transform coordinates. + * @method pointToWorld + * @param {Vec3} point + * @param {Vec3} result + * @return {Vec3} The "result" vector object + */ +Transform.prototype.pointToWorld = function(localPoint, result){ + return Transform.pointToWorldFrame(this.position, this.quaternion, localPoint, result); +}; + + +Transform.prototype.vectorToWorldFrame = function(localVector, result){ + var result = result || new Vec3(); + this.quaternion.vmult(localVector, result); + return result; +}; + +Transform.vectorToWorldFrame = function(quaternion, localVector, result){ + quaternion.vmult(localVector, result); + return result; +}; + +Transform.vectorToLocalFrame = function(position, quaternion, worldVector, result){ + var result = result || new Vec3(); + quaternion.w *= -1; + quaternion.vmult(worldVector, result); + quaternion.w *= -1; + return result; +}; + +},{"./Quaternion":28,"./Vec3":30}],30:[function(_dereq_,module,exports){ +module.exports = Vec3; + +var Mat3 = _dereq_('./Mat3'); + +/** + * 3-dimensional vector + * @class Vec3 + * @constructor + * @param {Number} x + * @param {Number} y + * @param {Number} z + * @author schteppe + * @example + * var v = new Vec3(1, 2, 3); + * console.log('x=' + v.x); // x=1 + */ +function Vec3(x,y,z){ + /** + * @property x + * @type {Number} + */ + this.x = x||0.0; + + /** + * @property y + * @type {Number} + */ + this.y = y||0.0; + + /** + * @property z + * @type {Number} + */ + this.z = z||0.0; +} + +/** + * @static + * @property {Vec3} ZERO + */ +Vec3.ZERO = new Vec3(0, 0, 0); + +/** + * @static + * @property {Vec3} UNIT_X + */ +Vec3.UNIT_X = new Vec3(1, 0, 0); + +/** + * @static + * @property {Vec3} UNIT_Y + */ +Vec3.UNIT_Y = new Vec3(0, 1, 0); + +/** + * @static + * @property {Vec3} UNIT_Z + */ +Vec3.UNIT_Z = new Vec3(0, 0, 1); + +/** + * Vector cross product + * @method cross + * @param {Vec3} v + * @param {Vec3} target Optional. Target to save in. + * @return {Vec3} + */ +Vec3.prototype.cross = function(v,target){ + var vx=v.x, vy=v.y, vz=v.z, x=this.x, y=this.y, z=this.z; + target = target || new Vec3(); + + target.x = (y * vz) - (z * vy); + target.y = (z * vx) - (x * vz); + target.z = (x * vy) - (y * vx); + + return target; +}; + +/** + * Set the vectors' 3 elements + * @method set + * @param {Number} x + * @param {Number} y + * @param {Number} z + * @return Vec3 + */ +Vec3.prototype.set = function(x,y,z){ + this.x = x; + this.y = y; + this.z = z; + return this; +}; + +/** + * Set all components of the vector to zero. + * @method setZero + */ +Vec3.prototype.setZero = function(){ + this.x = this.y = this.z = 0; +}; + +/** + * Vector addition + * @method vadd + * @param {Vec3} v + * @param {Vec3} target Optional. + * @return {Vec3} + */ +Vec3.prototype.vadd = function(v,target){ + if(target){ + target.x = v.x + this.x; + target.y = v.y + this.y; + target.z = v.z + this.z; + } else { + return new Vec3(this.x + v.x, + this.y + v.y, + this.z + v.z); + } +}; + +/** + * Vector subtraction + * @method vsub + * @param {Vec3} v + * @param {Vec3} target Optional. Target to save in. + * @return {Vec3} + */ +Vec3.prototype.vsub = function(v,target){ + if(target){ + target.x = this.x - v.x; + target.y = this.y - v.y; + target.z = this.z - v.z; + } else { + return new Vec3(this.x-v.x, + this.y-v.y, + this.z-v.z); + } +}; + +/** + * Get the cross product matrix a_cross from a vector, such that a x b = a_cross * b = c + * @method crossmat + * @see http://www8.cs.umu.se/kurser/TDBD24/VT06/lectures/Lecture6.pdf + * @return {Mat3} + */ +Vec3.prototype.crossmat = function(){ + return new Mat3([ 0, -this.z, this.y, + this.z, 0, -this.x, + -this.y, this.x, 0]); +}; + +/** + * Normalize the vector. Note that this changes the values in the vector. + * @method normalize + * @return {Number} Returns the norm of the vector + */ +Vec3.prototype.normalize = function(){ + var x=this.x, y=this.y, z=this.z; + var n = Math.sqrt(x*x + y*y + z*z); + if(n>0.0){ + var invN = 1/n; + this.x *= invN; + this.y *= invN; + this.z *= invN; + } else { + // Make something up + this.x = 0; + this.y = 0; + this.z = 0; + } + return n; +}; + +/** + * Get the version of this vector that is of length 1. + * @method unit + * @param {Vec3} target Optional target to save in + * @return {Vec3} Returns the unit vector + */ +Vec3.prototype.unit = function(target){ + target = target || new Vec3(); + var x=this.x, y=this.y, z=this.z; + var ninv = Math.sqrt(x*x + y*y + z*z); + if(ninv>0.0){ + ninv = 1.0/ninv; + target.x = x * ninv; + target.y = y * ninv; + target.z = z * ninv; + } else { + target.x = 1; + target.y = 0; + target.z = 0; + } + return target; +}; + +/** + * Get the length of the vector + * @method norm + * @return {Number} + * @deprecated Use .length() instead + */ +Vec3.prototype.norm = function(){ + var x=this.x, y=this.y, z=this.z; + return Math.sqrt(x*x + y*y + z*z); +}; + +/** + * Get the length of the vector + * @method length + * @return {Number} + */ +Vec3.prototype.length = Vec3.prototype.norm; + +/** + * Get the squared length of the vector + * @method norm2 + * @return {Number} + * @deprecated Use .lengthSquared() instead. + */ +Vec3.prototype.norm2 = function(){ + return this.dot(this); +}; + +/** + * Get the squared length of the vector. + * @method lengthSquared + * @return {Number} + */ +Vec3.prototype.lengthSquared = Vec3.prototype.norm2; + +/** + * Get distance from this point to another point + * @method distanceTo + * @param {Vec3} p + * @return {Number} + */ +Vec3.prototype.distanceTo = function(p){ + var x=this.x, y=this.y, z=this.z; + var px=p.x, py=p.y, pz=p.z; + return Math.sqrt((px-x)*(px-x)+ + (py-y)*(py-y)+ + (pz-z)*(pz-z)); +}; + +/** + * Get squared distance from this point to another point + * @method distanceSquared + * @param {Vec3} p + * @return {Number} + */ +Vec3.prototype.distanceSquared = function(p){ + var x=this.x, y=this.y, z=this.z; + var px=p.x, py=p.y, pz=p.z; + return (px-x)*(px-x) + (py-y)*(py-y) + (pz-z)*(pz-z); +}; + +/** + * Multiply all the components of the vector with a scalar. + * @deprecated Use .scale instead + * @method mult + * @param {Number} scalar + * @param {Vec3} target The vector to save the result in. + * @return {Vec3} + * @deprecated Use .scale() instead + */ +Vec3.prototype.mult = function(scalar,target){ + target = target || new Vec3(); + var x = this.x, + y = this.y, + z = this.z; + target.x = scalar * x; + target.y = scalar * y; + target.z = scalar * z; + return target; +}; + +/** + * Multiply the vector with a scalar. + * @method scale + * @param {Number} scalar + * @param {Vec3} target + * @return {Vec3} + */ +Vec3.prototype.scale = Vec3.prototype.mult; + +/** + * Calculate dot product + * @method dot + * @param {Vec3} v + * @return {Number} + */ +Vec3.prototype.dot = function(v){ + return this.x * v.x + this.y * v.y + this.z * v.z; +}; + +/** + * @method isZero + * @return bool + */ +Vec3.prototype.isZero = function(){ + return this.x===0 && this.y===0 && this.z===0; +}; + +/** + * Make the vector point in the opposite direction. + * @method negate + * @param {Vec3} target Optional target to save in + * @return {Vec3} + */ +Vec3.prototype.negate = function(target){ + target = target || new Vec3(); + target.x = -this.x; + target.y = -this.y; + target.z = -this.z; + return target; +}; + +/** + * Compute two artificial tangents to the vector + * @method tangents + * @param {Vec3} t1 Vector object to save the first tangent in + * @param {Vec3} t2 Vector object to save the second tangent in + */ +var Vec3_tangents_n = new Vec3(); +var Vec3_tangents_randVec = new Vec3(); +Vec3.prototype.tangents = function(t1,t2){ + var norm = this.norm(); + if(norm>0.0){ + var n = Vec3_tangents_n; + var inorm = 1/norm; + n.set(this.x*inorm,this.y*inorm,this.z*inorm); + var randVec = Vec3_tangents_randVec; + if(Math.abs(n.x) < 0.9){ + randVec.set(1,0,0); + n.cross(randVec,t1); + } else { + randVec.set(0,1,0); + n.cross(randVec,t1); + } + n.cross(t1,t2); + } else { + // The normal length is zero, make something up + t1.set(1, 0, 0); + t2.set(0, 1, 0); + } +}; + +/** + * Converts to a more readable format + * @method toString + * @return string + */ +Vec3.prototype.toString = function(){ + return this.x+","+this.y+","+this.z; +}; + +/** + * Converts to an array + * @method toArray + * @return Array + */ +Vec3.prototype.toArray = function(){ + return [this.x, this.y, this.z]; +}; + +/** + * Copies value of source to this vector. + * @method copy + * @param {Vec3} source + * @return {Vec3} this + */ +Vec3.prototype.copy = function(source){ + this.x = source.x; + this.y = source.y; + this.z = source.z; + return this; +}; + + +/** + * Do a linear interpolation between two vectors + * @method lerp + * @param {Vec3} v + * @param {Number} t A number between 0 and 1. 0 will make this function return u, and 1 will make it return v. Numbers in between will generate a vector in between them. + * @param {Vec3} target + */ +Vec3.prototype.lerp = function(v,t,target){ + var x=this.x, y=this.y, z=this.z; + target.x = x + (v.x-x)*t; + target.y = y + (v.y-y)*t; + target.z = z + (v.z-z)*t; +}; + +/** + * Check if a vector equals is almost equal to another one. + * @method almostEquals + * @param {Vec3} v + * @param {Number} precision + * @return bool + */ +Vec3.prototype.almostEquals = function(v,precision){ + if(precision===undefined){ + precision = 1e-6; + } + if( Math.abs(this.x-v.x)>precision || + Math.abs(this.y-v.y)>precision || + Math.abs(this.z-v.z)>precision){ + return false; + } + return true; +}; + +/** + * Check if a vector is almost zero + * @method almostZero + * @param {Number} precision + */ +Vec3.prototype.almostZero = function(precision){ + if(precision===undefined){ + precision = 1e-6; + } + if( Math.abs(this.x)>precision || + Math.abs(this.y)>precision || + Math.abs(this.z)>precision){ + return false; + } + return true; +}; + +var antip_neg = new Vec3(); + +/** + * Check if the vector is anti-parallel to another vector. + * @method isAntiparallelTo + * @param {Vec3} v + * @param {Number} precision Set to zero for exact comparisons + * @return {Boolean} + */ +Vec3.prototype.isAntiparallelTo = function(v,precision){ + this.negate(antip_neg); + return antip_neg.almostEquals(v,precision); +}; + +/** + * Clone the vector + * @method clone + * @return {Vec3} + */ +Vec3.prototype.clone = function(){ + return new Vec3(this.x, this.y, this.z); +}; +},{"./Mat3":27}],31:[function(_dereq_,module,exports){ +module.exports = Body; + +var EventTarget = _dereq_('../utils/EventTarget'); +var Shape = _dereq_('../shapes/Shape'); +var Vec3 = _dereq_('../math/Vec3'); +var Mat3 = _dereq_('../math/Mat3'); +var Quaternion = _dereq_('../math/Quaternion'); +var Material = _dereq_('../material/Material'); +var AABB = _dereq_('../collision/AABB'); +var Box = _dereq_('../shapes/Box'); + +/** + * Base class for all body types. + * @class Body + * @constructor + * @extends EventTarget + * @param {object} [options] + * @param {Vec3} [options.position] + * @param {Vec3} [options.velocity] + * @param {Vec3} [options.angularVelocity] + * @param {Quaternion} [options.quaternion] + * @param {number} [options.mass] + * @param {Material} [options.material] + * @param {number} [options.type] + * @param {number} [options.linearDamping=0.01] + * @param {number} [options.angularDamping=0.01] + * @param {boolean} [options.allowSleep=true] + * @param {number} [options.sleepSpeedLimit=0.1] + * @param {number} [options.sleepTimeLimit=1] + * @param {number} [options.collisionFilterGroup=1] + * @param {number} [options.collisionFilterMask=1] + * @param {boolean} [options.fixedRotation=false] + * @param {Body} [options.shape] + * @example + * var body = new Body({ + * mass: 1 + * }); + * var shape = new Sphere(1); + * body.addShape(shape); + * world.add(body); + */ +function Body(options){ + options = options || {}; + + EventTarget.apply(this); + + this.id = Body.idCounter++; + + /** + * Reference to the world the body is living in + * @property world + * @type {World} + */ + this.world = null; + + /** + * Callback function that is used BEFORE stepping the system. Use it to apply forces, for example. Inside the function, "this" will refer to this Body object. + * @property preStep + * @type {Function} + * @deprecated Use World events instead + */ + this.preStep = null; + + /** + * Callback function that is used AFTER stepping the system. Inside the function, "this" will refer to this Body object. + * @property postStep + * @type {Function} + * @deprecated Use World events instead + */ + this.postStep = null; + + this.vlambda = new Vec3(); + + /** + * @property {Number} collisionFilterGroup + */ + this.collisionFilterGroup = typeof(options.collisionFilterGroup) === 'number' ? options.collisionFilterGroup : 1; + + /** + * @property {Number} collisionFilterMask + */ + this.collisionFilterMask = typeof(options.collisionFilterMask) === 'number' ? options.collisionFilterMask : 1; + + /** + * Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled. + * @property {Number} collisionResponse + */ + this.collisionResponse = true; + + /** + * @property position + * @type {Vec3} + */ + this.position = new Vec3(); + + if(options.position){ + this.position.copy(options.position); + } + + /** + * @property {Vec3} previousPosition + */ + this.previousPosition = new Vec3(); + + /** + * Initial position of the body + * @property initPosition + * @type {Vec3} + */ + this.initPosition = new Vec3(); + + /** + * @property velocity + * @type {Vec3} + */ + this.velocity = new Vec3(); + + if(options.velocity){ + this.velocity.copy(options.velocity); + } + + /** + * @property initVelocity + * @type {Vec3} + */ + this.initVelocity = new Vec3(); + + /** + * Linear force on the body + * @property force + * @type {Vec3} + */ + this.force = new Vec3(); + + var mass = typeof(options.mass) === 'number' ? options.mass : 0; + + /** + * @property mass + * @type {Number} + * @default 0 + */ + this.mass = mass; + + /** + * @property invMass + * @type {Number} + */ + this.invMass = mass > 0 ? 1.0 / mass : 0; + + /** + * @property material + * @type {Material} + */ + this.material = options.material || null; + + /** + * @property linearDamping + * @type {Number} + */ + this.linearDamping = typeof(options.linearDamping) === 'number' ? options.linearDamping : 0.01; + + /** + * One of: Body.DYNAMIC, Body.STATIC and Body.KINEMATIC. + * @property type + * @type {Number} + */ + this.type = (mass <= 0.0 ? Body.STATIC : Body.DYNAMIC); + if(typeof(options.type) === typeof(Body.STATIC)){ + this.type = options.type; + } + + /** + * If true, the body will automatically fall to sleep. + * @property allowSleep + * @type {Boolean} + * @default true + */ + this.allowSleep = typeof(options.allowSleep) !== 'undefined' ? options.allowSleep : true; + + /** + * Current sleep state. + * @property sleepState + * @type {Number} + */ + this.sleepState = 0; + + /** + * If the speed (the norm of the velocity) is smaller than this value, the body is considered sleepy. + * @property sleepSpeedLimit + * @type {Number} + * @default 0.1 + */ + this.sleepSpeedLimit = typeof(options.sleepSpeedLimit) !== 'undefined' ? options.sleepSpeedLimit : 0.1; + + /** + * If the body has been sleepy for this sleepTimeLimit seconds, it is considered sleeping. + * @property sleepTimeLimit + * @type {Number} + * @default 1 + */ + this.sleepTimeLimit = typeof(options.sleepTimeLimit) !== 'undefined' ? options.sleepTimeLimit : 1; + + this.timeLastSleepy = 0; + + this._wakeUpAfterNarrowphase = false; + + + /** + * Rotational force on the body, around center of mass + * @property {Vec3} torque + */ + this.torque = new Vec3(); + + /** + * Orientation of the body + * @property quaternion + * @type {Quaternion} + */ + this.quaternion = new Quaternion(); + + if(options.quaternion){ + this.quaternion.copy(options.quaternion); + } + + /** + * @property initQuaternion + * @type {Quaternion} + */ + this.initQuaternion = new Quaternion(); + + /** + * @property angularVelocity + * @type {Vec3} + */ + this.angularVelocity = new Vec3(); + + if(options.angularVelocity){ + this.angularVelocity.copy(options.angularVelocity); + } + + /** + * @property initAngularVelocity + * @type {Vec3} + */ + this.initAngularVelocity = new Vec3(); + + this.interpolatedPosition = new Vec3(); + this.interpolatedQuaternion = new Quaternion(); + + /** + * @property shapes + * @type {array} + */ + this.shapes = []; + + /** + * @property shapeOffsets + * @type {array} + */ + this.shapeOffsets = []; + + /** + * @property shapeOrientations + * @type {array} + */ + this.shapeOrientations = []; + + /** + * @property inertia + * @type {Vec3} + */ + this.inertia = new Vec3(); + + /** + * @property {Vec3} invInertia + */ + this.invInertia = new Vec3(); + + /** + * @property {Mat3} invInertiaWorld + */ + this.invInertiaWorld = new Mat3(); + + this.invMassSolve = 0; + + /** + * @property {Vec3} invInertiaSolve + */ + this.invInertiaSolve = new Vec3(); + + /** + * @property {Mat3} invInertiaWorldSolve + */ + this.invInertiaWorldSolve = new Mat3(); + + /** + * Set to true if you don't want the body to rotate. Make sure to run .updateMassProperties() after changing this. + * @property {Boolean} fixedRotation + * @default false + */ + this.fixedRotation = typeof(options.fixedRotation) !== "undefined" ? options.fixedRotation : false; + + /** + * @property {Number} angularDamping + */ + this.angularDamping = typeof(options.angularDamping) !== 'undefined' ? options.angularDamping : 0.01; + + /** + * @property aabb + * @type {AABB} + */ + this.aabb = new AABB(); + + /** + * Indicates if the AABB needs to be updated before use. + * @property aabbNeedsUpdate + * @type {Boolean} + */ + this.aabbNeedsUpdate = true; + + this.wlambda = new Vec3(); + + if(options.shape){ + this.addShape(options.shape); + } + + this.updateMassProperties(); +} +Body.prototype = new EventTarget(); +Body.prototype.constructor = Body; + +/** + * A dynamic body is fully simulated. Can be moved manually by the user, but normally they move according to forces. A dynamic body can collide with all body types. A dynamic body always has finite, non-zero mass. + * @static + * @property DYNAMIC + * @type {Number} + */ +Body.DYNAMIC = 1; + +/** + * A static body does not move during simulation and behaves as if it has infinite mass. Static bodies can be moved manually by setting the position of the body. The velocity of a static body is always zero. Static bodies do not collide with other static or kinematic bodies. + * @static + * @property STATIC + * @type {Number} + */ +Body.STATIC = 2; + +/** + * A kinematic body moves under simulation according to its velocity. They do not respond to forces. They can be moved manually, but normally a kinematic body is moved by setting its velocity. A kinematic body behaves as if it has infinite mass. Kinematic bodies do not collide with other static or kinematic bodies. + * @static + * @property KINEMATIC + * @type {Number} + */ +Body.KINEMATIC = 4; + + + +/** + * @static + * @property AWAKE + * @type {number} + */ +Body.AWAKE = 0; + +/** + * @static + * @property SLEEPY + * @type {number} + */ +Body.SLEEPY = 1; + +/** + * @static + * @property SLEEPING + * @type {number} + */ +Body.SLEEPING = 2; + +Body.idCounter = 0; + +/** + * Wake the body up. + * @method wakeUp + */ +Body.prototype.wakeUp = function(){ + var s = this.sleepState; + this.sleepState = 0; + if(s === Body.SLEEPING){ + this.dispatchEvent({type:"wakeup"}); + } +}; + +/** + * Force body sleep + * @method sleep + */ +Body.prototype.sleep = function(){ + this.sleepState = Body.SLEEPING; + this.velocity.set(0,0,0); + this.angularVelocity.set(0,0,0); +}; + +Body.sleepyEvent = { + type: "sleepy" +}; + +Body.sleepEvent = { + type: "sleep" +}; + +/** + * Called every timestep to update internal sleep timer and change sleep state if needed. + * @method sleepTick + * @param {Number} time The world time in seconds + */ +Body.prototype.sleepTick = function(time){ + if(this.allowSleep){ + var sleepState = this.sleepState; + var speedSquared = this.velocity.norm2() + this.angularVelocity.norm2(); + var speedLimitSquared = Math.pow(this.sleepSpeedLimit,2); + if(sleepState===Body.AWAKE && speedSquared < speedLimitSquared){ + this.sleepState = Body.SLEEPY; // Sleepy + this.timeLastSleepy = time; + this.dispatchEvent(Body.sleepyEvent); + } else if(sleepState===Body.SLEEPY && speedSquared > speedLimitSquared){ + this.wakeUp(); // Wake up + } else if(sleepState===Body.SLEEPY && (time - this.timeLastSleepy ) > this.sleepTimeLimit){ + this.sleep(); // Sleeping + this.dispatchEvent(Body.sleepEvent); + } + } +}; + +/** + * If the body is sleeping, it should be immovable / have infinite mass during solve. We solve it by having a separate "solve mass". + * @method updateSolveMassProperties + */ +Body.prototype.updateSolveMassProperties = function(){ + if(this.sleepState === Body.SLEEPING || this.type === Body.KINEMATIC){ + this.invMassSolve = 0; + this.invInertiaSolve.setZero(); + this.invInertiaWorldSolve.setZero(); + } else { + this.invMassSolve = this.invMass; + this.invInertiaSolve.copy(this.invInertia); + this.invInertiaWorldSolve.copy(this.invInertiaWorld); + } +}; + +/** + * Convert a world point to local body frame. + * @method pointToLocalFrame + * @param {Vec3} worldPoint + * @param {Vec3} result + * @return {Vec3} + */ +Body.prototype.pointToLocalFrame = function(worldPoint,result){ + var result = result || new Vec3(); + worldPoint.vsub(this.position,result); + this.quaternion.conjugate().vmult(result,result); + return result; +}; + +/** + * Convert a world vector to local body frame. + * @method vectorToLocalFrame + * @param {Vec3} worldPoint + * @param {Vec3} result + * @return {Vec3} + */ +Body.prototype.vectorToLocalFrame = function(worldVector, result){ + var result = result || new Vec3(); + this.quaternion.conjugate().vmult(worldVector,result); + return result; +}; + +/** + * Convert a local body point to world frame. + * @method pointToWorldFrame + * @param {Vec3} localPoint + * @param {Vec3} result + * @return {Vec3} + */ +Body.prototype.pointToWorldFrame = function(localPoint,result){ + var result = result || new Vec3(); + this.quaternion.vmult(localPoint,result); + result.vadd(this.position,result); + return result; +}; + +/** + * Convert a local body point to world frame. + * @method vectorToWorldFrame + * @param {Vec3} localVector + * @param {Vec3} result + * @return {Vec3} + */ +Body.prototype.vectorToWorldFrame = function(localVector, result){ + var result = result || new Vec3(); + this.quaternion.vmult(localVector, result); + return result; +}; + +var tmpVec = new Vec3(); +var tmpQuat = new Quaternion(); + +/** + * Add a shape to the body with a local offset and orientation. + * @method addShape + * @param {Shape} shape + * @param {Vec3} offset + * @param {Quaternion} quaternion + * @return {Body} The body object, for chainability. + */ +Body.prototype.addShape = function(shape, _offset, _orientation){ + var offset = new Vec3(); + var orientation = new Quaternion(); + + if(_offset){ + offset.copy(_offset); + } + if(_orientation){ + orientation.copy(_orientation); + } + + this.shapes.push(shape); + this.shapeOffsets.push(offset); + this.shapeOrientations.push(orientation); + this.updateMassProperties(); + this.updateBoundingRadius(); + + this.aabbNeedsUpdate = true; + + return this; +}; + +/** + * Update the bounding radius of the body. Should be done if any of the shapes are changed. + * @method updateBoundingRadius + */ +Body.prototype.updateBoundingRadius = function(){ + var shapes = this.shapes, + shapeOffsets = this.shapeOffsets, + N = shapes.length, + radius = 0; + + for(var i=0; i!==N; i++){ + var shape = shapes[i]; + shape.updateBoundingSphereRadius(); + var offset = shapeOffsets[i].norm(), + r = shape.boundingSphereRadius; + if(offset + r > radius){ + radius = offset + r; + } + } + + this.boundingRadius = radius; +}; + +var computeAABB_shapeAABB = new AABB(); + +/** + * Updates the .aabb + * @method computeAABB + * @todo rename to updateAABB() + */ +Body.prototype.computeAABB = function(){ + var shapes = this.shapes, + shapeOffsets = this.shapeOffsets, + shapeOrientations = this.shapeOrientations, + N = shapes.length, + offset = tmpVec, + orientation = tmpQuat, + bodyQuat = this.quaternion, + aabb = this.aabb, + shapeAABB = computeAABB_shapeAABB; + + for(var i=0; i!==N; i++){ + var shape = shapes[i]; + + // Get shape world quaternion + shapeOrientations[i].mult(bodyQuat, orientation); + + // Get shape world position + orientation.vmult(shapeOffsets[i], offset); + offset.vadd(this.position, offset); + + // vec2.rotate(offset, shapeOffsets[i], bodyAngle); + // vec2.add(offset, offset, this.position); + + // Get shape AABB + shape.calculateWorldAABB(offset, orientation, shapeAABB.lowerBound, shapeAABB.upperBound); + + if(i === 0){ + aabb.copy(shapeAABB); + } else { + aabb.extend(shapeAABB); + } + } + + this.aabbNeedsUpdate = false; +}; + +var uiw_m1 = new Mat3(), + uiw_m2 = new Mat3(), + uiw_m3 = new Mat3(); + +/** + * Update .inertiaWorld and .invInertiaWorld + * @method updateInertiaWorld + */ +Body.prototype.updateInertiaWorld = function(force){ + var I = this.invInertia; + if (I.x === I.y && I.y === I.z && !force) { + // If inertia M = s*I, where I is identity and s a scalar, then + // R*M*R' = R*(s*I)*R' = s*R*I*R' = s*R*R' = s*I = M + // where R is the rotation matrix. + // In other words, we don't have to transform the inertia if all + // inertia diagonal entries are equal. + } else { + var m1 = uiw_m1, + m2 = uiw_m2, + m3 = uiw_m3; + m1.setRotationFromQuaternion(this.quaternion); + m1.transpose(m2); + m1.scale(I,m1); + m1.mmult(m2,this.invInertiaWorld); + //m3.getTrace(this.invInertiaWorld); + } + + /* + this.quaternion.vmult(this.inertia,this.inertiaWorld); + this.quaternion.vmult(this.invInertia,this.invInertiaWorld); + */ +}; + +/** + * Apply force to a world point. This could for example be a point on the Body surface. Applying force this way will add to Body.force and Body.torque. + * @method applyForce + * @param {Vec3} force The amount of force to add. + * @param {Vec3} worldPoint A world point to apply the force on. + */ +var Body_applyForce_r = new Vec3(); +var Body_applyForce_rotForce = new Vec3(); +Body.prototype.applyForce = function(force,worldPoint){ + if(this.type !== Body.DYNAMIC){ + return; + } + + // Compute point position relative to the body center + var r = Body_applyForce_r; + worldPoint.vsub(this.position,r); + + // Compute produced rotational force + var rotForce = Body_applyForce_rotForce; + r.cross(force,rotForce); + + // Add linear force + this.force.vadd(force,this.force); + + // Add rotational force + this.torque.vadd(rotForce,this.torque); +}; + +/** + * Apply force to a local point in the body. + * @method applyLocalForce + * @param {Vec3} force The force vector to apply, defined locally in the body frame. + * @param {Vec3} localPoint A local point in the body to apply the force on. + */ +var Body_applyLocalForce_worldForce = new Vec3(); +var Body_applyLocalForce_worldPoint = new Vec3(); +Body.prototype.applyLocalForce = function(localForce, localPoint){ + if(this.type !== Body.DYNAMIC){ + return; + } + + var worldForce = Body_applyLocalForce_worldForce; + var worldPoint = Body_applyLocalForce_worldPoint; + + // Transform the force vector to world space + this.vectorToWorldFrame(localForce, worldForce); + this.pointToWorldFrame(localPoint, worldPoint); + + this.applyForce(worldForce, worldPoint); +}; + +/** + * Apply impulse to a world point. This could for example be a point on the Body surface. An impulse is a force added to a body during a short period of time (impulse = force * time). Impulses will be added to Body.velocity and Body.angularVelocity. + * @method applyImpulse + * @param {Vec3} impulse The amount of impulse to add. + * @param {Vec3} worldPoint A world point to apply the force on. + */ +var Body_applyImpulse_r = new Vec3(); +var Body_applyImpulse_velo = new Vec3(); +var Body_applyImpulse_rotVelo = new Vec3(); +Body.prototype.applyImpulse = function(impulse, worldPoint){ + if(this.type !== Body.DYNAMIC){ + return; + } + + // Compute point position relative to the body center + var r = Body_applyImpulse_r; + worldPoint.vsub(this.position,r); + + // Compute produced central impulse velocity + var velo = Body_applyImpulse_velo; + velo.copy(impulse); + velo.mult(this.invMass,velo); + + // Add linear impulse + this.velocity.vadd(velo, this.velocity); + + // Compute produced rotational impulse velocity + var rotVelo = Body_applyImpulse_rotVelo; + r.cross(impulse,rotVelo); + + /* + rotVelo.x *= this.invInertia.x; + rotVelo.y *= this.invInertia.y; + rotVelo.z *= this.invInertia.z; + */ + this.invInertiaWorld.vmult(rotVelo,rotVelo); + + // Add rotational Impulse + this.angularVelocity.vadd(rotVelo, this.angularVelocity); +}; + +/** + * Apply locally-defined impulse to a local point in the body. + * @method applyLocalImpulse + * @param {Vec3} force The force vector to apply, defined locally in the body frame. + * @param {Vec3} localPoint A local point in the body to apply the force on. + */ +var Body_applyLocalImpulse_worldImpulse = new Vec3(); +var Body_applyLocalImpulse_worldPoint = new Vec3(); +Body.prototype.applyLocalImpulse = function(localImpulse, localPoint){ + if(this.type !== Body.DYNAMIC){ + return; + } + + var worldImpulse = Body_applyLocalImpulse_worldImpulse; + var worldPoint = Body_applyLocalImpulse_worldPoint; + + // Transform the force vector to world space + this.vectorToWorldFrame(localImpulse, worldImpulse); + this.pointToWorldFrame(localPoint, worldPoint); + + this.applyImpulse(worldImpulse, worldPoint); +}; + +var Body_updateMassProperties_halfExtents = new Vec3(); + +/** + * Should be called whenever you change the body shape or mass. + * @method updateMassProperties + */ +Body.prototype.updateMassProperties = function(){ + var halfExtents = Body_updateMassProperties_halfExtents; + + this.invMass = this.mass > 0 ? 1.0 / this.mass : 0; + var I = this.inertia; + var fixed = this.fixedRotation; + + // Approximate with AABB box + this.computeAABB(); + halfExtents.set( + (this.aabb.upperBound.x-this.aabb.lowerBound.x) / 2, + (this.aabb.upperBound.y-this.aabb.lowerBound.y) / 2, + (this.aabb.upperBound.z-this.aabb.lowerBound.z) / 2 + ); + Box.calculateInertia(halfExtents, this.mass, I); + + this.invInertia.set( + I.x > 0 && !fixed ? 1.0 / I.x : 0, + I.y > 0 && !fixed ? 1.0 / I.y : 0, + I.z > 0 && !fixed ? 1.0 / I.z : 0 + ); + this.updateInertiaWorld(true); +}; + +/** + * Get world velocity of a point in the body. + * @method getVelocityAtWorldPoint + * @param {Vec3} worldPoint + * @param {Vec3} result + * @return {Vec3} The result vector. + */ +Body.prototype.getVelocityAtWorldPoint = function(worldPoint, result){ + var r = new Vec3(); + worldPoint.vsub(this.position, r); + this.angularVelocity.cross(r, result); + this.velocity.vadd(result, result); + return result; +}; + +},{"../collision/AABB":3,"../material/Material":25,"../math/Mat3":27,"../math/Quaternion":28,"../math/Vec3":30,"../shapes/Box":37,"../shapes/Shape":43,"../utils/EventTarget":49}],32:[function(_dereq_,module,exports){ +var Body = _dereq_('./Body'); +var Vec3 = _dereq_('../math/Vec3'); +var Quaternion = _dereq_('../math/Quaternion'); +var RaycastResult = _dereq_('../collision/RaycastResult'); +var Ray = _dereq_('../collision/Ray'); +var WheelInfo = _dereq_('../objects/WheelInfo'); + +module.exports = RaycastVehicle; + +/** + * Vehicle helper class that casts rays from the wheel positions towards the ground and applies forces. + * @class RaycastVehicle + * @constructor + * @param {object} [options] + * @param {Body} [options.chassisBody] The car chassis body. + * @param {integer} [options.indexRightAxis] Axis to use for right. x=0, y=1, z=2 + * @param {integer} [options.indexLeftAxis] + * @param {integer} [options.indexUpAxis] + */ +function RaycastVehicle(options){ + + /** + * @property {Body} chassisBody + */ + this.chassisBody = options.chassisBody; + + /** + * An array of WheelInfo objects. + * @property {array} wheelInfos + */ + this.wheelInfos = []; + + /** + * Will be set to true if the car is sliding. + * @property {boolean} sliding + */ + this.sliding = false; + + /** + * @property {World} world + */ + this.world = null; + + /** + * Index of the right axis, 0=x, 1=y, 2=z + * @property {integer} indexRightAxis + * @default 1 + */ + this.indexRightAxis = typeof(options.indexRightAxis) !== 'undefined' ? options.indexRightAxis : 1; + + /** + * Index of the forward axis, 0=x, 1=y, 2=z + * @property {integer} indexForwardAxis + * @default 0 + */ + this.indexForwardAxis = typeof(options.indexForwardAxis) !== 'undefined' ? options.indexForwardAxis : 0; + + /** + * Index of the up axis, 0=x, 1=y, 2=z + * @property {integer} indexUpAxis + * @default 2 + */ + this.indexUpAxis = typeof(options.indexUpAxis) !== 'undefined' ? options.indexUpAxis : 2; +} + +var tmpVec1 = new Vec3(); +var tmpVec2 = new Vec3(); +var tmpVec3 = new Vec3(); +var tmpVec4 = new Vec3(); +var tmpVec5 = new Vec3(); +var tmpVec6 = new Vec3(); +var tmpRay = new Ray(); + +/** + * Add a wheel. For information about the options, see WheelInfo. + * @method addWheel + * @param {object} [options] + */ +RaycastVehicle.prototype.addWheel = function(options){ + options = options || {}; + + var info = new WheelInfo(options); + var index = this.wheelInfos.length; + this.wheelInfos.push(info); + + return index; +}; + +/** + * Set the steering value of a wheel. + * @method setSteeringValue + * @param {number} value + * @param {integer} wheelIndex + */ +RaycastVehicle.prototype.setSteeringValue = function(value, wheelIndex){ + var wheel = this.wheelInfos[wheelIndex]; + wheel.steering = value; +}; + +var torque = new Vec3(); + +/** + * Set the wheel force to apply on one of the wheels each time step + * @method applyEngineForce + * @param {number} value + * @param {integer} wheelIndex + */ +RaycastVehicle.prototype.applyEngineForce = function(value, wheelIndex){ + this.wheelInfos[wheelIndex].engineForce = value; +}; + +/** + * Set the braking force of a wheel + * @method setBrake + * @param {number} brake + * @param {integer} wheelIndex + */ +RaycastVehicle.prototype.setBrake = function(brake, wheelIndex){ + this.wheelInfos[wheelIndex].brake = brake; +}; + +/** + * Add the vehicle including its constraints to the world. + * @method addToWorld + * @param {World} world + */ +RaycastVehicle.prototype.addToWorld = function(world){ + var constraints = this.constraints; + world.add(this.chassisBody); + var that = this; + this.preStepCallback = function(){ + that.updateVehicle(world.dt); + }; + world.addEventListener('preStep', this.preStepCallback); + this.world = world; +}; + +/** + * Get one of the wheel axles, world-oriented. + * @private + * @method getVehicleAxisWorld + * @param {integer} axisIndex + * @param {Vec3} result + */ +RaycastVehicle.prototype.getVehicleAxisWorld = function(axisIndex, result){ + result.set( + axisIndex === 0 ? 1 : 0, + axisIndex === 1 ? 1 : 0, + axisIndex === 2 ? 1 : 0 + ); + this.chassisBody.vectorToWorldFrame(result, result); +}; + +RaycastVehicle.prototype.updateVehicle = function(timeStep){ + var wheelInfos = this.wheelInfos; + var numWheels = wheelInfos.length; + var chassisBody = this.chassisBody; + + for (var i = 0; i < numWheels; i++) { + this.updateWheelTransform(i); + } + + this.currentVehicleSpeedKmHour = 3.6 * chassisBody.velocity.norm(); + + var forwardWorld = new Vec3(); + this.getVehicleAxisWorld(this.indexForwardAxis, forwardWorld); + + if (forwardWorld.dot(chassisBody.velocity) < 0){ + this.currentVehicleSpeedKmHour *= -1; + } + + // simulate suspension + for (var i = 0; i < numWheels; i++) { + this.castRay(wheelInfos[i]); + } + + this.updateSuspension(timeStep); + + var impulse = new Vec3(); + var relpos = new Vec3(); + for (var i = 0; i < numWheels; i++) { + //apply suspension force + var wheel = wheelInfos[i]; + var suspensionForce = wheel.suspensionForce; + if (suspensionForce > wheel.maxSuspensionForce) { + suspensionForce = wheel.maxSuspensionForce; + } + wheel.raycastResult.hitNormalWorld.scale(suspensionForce * timeStep, impulse); + + wheel.raycastResult.hitPointWorld.vsub(chassisBody.position, relpos); + chassisBody.applyImpulse(impulse, wheel.raycastResult.hitPointWorld/*relpos*/); + } + + this.updateFriction(timeStep); + + var hitNormalWorldScaledWithProj = new Vec3(); + var fwd = new Vec3(); + var vel = new Vec3(); + for (i = 0; i < numWheels; i++) { + var wheel = wheelInfos[i]; + //var relpos = new Vec3(); + //wheel.chassisConnectionPointWorld.vsub(chassisBody.position, relpos); + chassisBody.getVelocityAtWorldPoint(wheel.chassisConnectionPointWorld, vel); + + // Hack to get the rotation in the correct direction + var m = 1; + switch(this.indexUpAxis){ + case 1: + m = -1; + break; + } + + if (wheel.isInContact) { + + this.getVehicleAxisWorld(this.indexForwardAxis, fwd); + var proj = fwd.dot(wheel.raycastResult.hitNormalWorld); + wheel.raycastResult.hitNormalWorld.scale(proj, hitNormalWorldScaledWithProj); + + fwd.vsub(hitNormalWorldScaledWithProj, fwd); + + var proj2 = fwd.dot(vel); + wheel.deltaRotation = m * proj2 * timeStep / wheel.radius; + } + + if((wheel.sliding || !wheel.isInContact) && wheel.engineForce !== 0 && wheel.useCustomSlidingRotationalSpeed){ + // Apply custom rotation when accelerating and sliding + wheel.deltaRotation = (wheel.engineForce > 0 ? 1 : -1) * wheel.customSlidingRotationalSpeed * timeStep; + } + + // Lock wheels + if(Math.abs(wheel.brake) > Math.abs(wheel.engineForce)){ + wheel.deltaRotation = 0; + } + + wheel.rotation += wheel.deltaRotation; // Use the old value + wheel.deltaRotation *= 0.99; // damping of rotation when not in contact + } +}; + +RaycastVehicle.prototype.updateSuspension = function(deltaTime) { + var chassisBody = this.chassisBody; + var chassisMass = chassisBody.mass; + var wheelInfos = this.wheelInfos; + var numWheels = wheelInfos.length; + + for (var w_it = 0; w_it < numWheels; w_it++){ + var wheel = wheelInfos[w_it]; + + if (wheel.isInContact){ + var force; + + // Spring + var susp_length = wheel.suspensionRestLength; + var current_length = wheel.suspensionLength; + var length_diff = (susp_length - current_length); + + force = wheel.suspensionStiffness * length_diff * wheel.clippedInvContactDotSuspension; + + // Damper + var projected_rel_vel = wheel.suspensionRelativeVelocity; + var susp_damping; + if (projected_rel_vel < 0) { + susp_damping = wheel.dampingCompression; + } else { + susp_damping = wheel.dampingRelaxation; + } + force -= susp_damping * projected_rel_vel; + + wheel.suspensionForce = force * chassisMass; + if (wheel.suspensionForce < 0) { + wheel.suspensionForce = 0; + } + } else { + wheel.suspensionForce = 0; + } + } +}; + +/** + * Remove the vehicle including its constraints from the world. + * @method removeFromWorld + * @param {World} world + */ +RaycastVehicle.prototype.removeFromWorld = function(world){ + var constraints = this.constraints; + world.remove(this.chassisBody); + world.removeEventListener('preStep', this.preStepCallback); + this.world = null; +}; + +var castRay_rayvector = new Vec3(); +var castRay_target = new Vec3(); +RaycastVehicle.prototype.castRay = function(wheel) { + var rayvector = castRay_rayvector; + var target = castRay_target; + + this.updateWheelTransformWorld(wheel); + var chassisBody = this.chassisBody; + + var depth = -1; + + var raylen = wheel.suspensionRestLength + wheel.radius; + + wheel.directionWorld.scale(raylen, rayvector); + var source = wheel.chassisConnectionPointWorld; + source.vadd(rayvector, target); + var raycastResult = wheel.raycastResult; + + var param = 0; + + raycastResult.reset(); + // Turn off ray collision with the chassis temporarily + var oldState = chassisBody.collisionResponse; + chassisBody.collisionResponse = false; + + // Cast ray against world + this.world.rayTest(source, target, raycastResult); + chassisBody.collisionResponse = oldState; + + var object = raycastResult.body; + + wheel.raycastResult.groundObject = 0; + + if (object) { + depth = raycastResult.distance; + wheel.raycastResult.hitNormalWorld = raycastResult.hitNormalWorld; + wheel.isInContact = true; + + var hitDistance = raycastResult.distance; + wheel.suspensionLength = hitDistance - wheel.radius; + + // clamp on max suspension travel + var minSuspensionLength = wheel.suspensionRestLength - wheel.maxSuspensionTravel; + var maxSuspensionLength = wheel.suspensionRestLength + wheel.maxSuspensionTravel; + if (wheel.suspensionLength < minSuspensionLength) { + wheel.suspensionLength = minSuspensionLength; + } + if (wheel.suspensionLength > maxSuspensionLength) { + wheel.suspensionLength = maxSuspensionLength; + wheel.raycastResult.reset(); + } + + var denominator = wheel.raycastResult.hitNormalWorld.dot(wheel.directionWorld); + + var chassis_velocity_at_contactPoint = new Vec3(); + chassisBody.getVelocityAtWorldPoint(wheel.raycastResult.hitPointWorld, chassis_velocity_at_contactPoint); + + var projVel = wheel.raycastResult.hitNormalWorld.dot( chassis_velocity_at_contactPoint ); + + if (denominator >= -0.1) { + wheel.suspensionRelativeVelocity = 0; + wheel.clippedInvContactDotSuspension = 1 / 0.1; + } else { + var inv = -1 / denominator; + wheel.suspensionRelativeVelocity = projVel * inv; + wheel.clippedInvContactDotSuspension = inv; + } + + } else { + + //put wheel info as in rest position + wheel.suspensionLength = wheel.suspensionRestLength + 0 * wheel.maxSuspensionTravel; + wheel.suspensionRelativeVelocity = 0.0; + wheel.directionWorld.scale(-1, wheel.raycastResult.hitNormalWorld); + wheel.clippedInvContactDotSuspension = 1.0; + } + + return depth; +}; + +RaycastVehicle.prototype.updateWheelTransformWorld = function(wheel){ + wheel.isInContact = false; + var chassisBody = this.chassisBody; + chassisBody.pointToWorldFrame(wheel.chassisConnectionPointLocal, wheel.chassisConnectionPointWorld); + chassisBody.vectorToWorldFrame(wheel.directionLocal, wheel.directionWorld); + chassisBody.vectorToWorldFrame(wheel.axleLocal, wheel.axleWorld); +}; + + +/** + * Update one of the wheel transform. + * Note when rendering wheels: during each step, wheel transforms are updated BEFORE the chassis; ie. their position becomes invalid after the step. Thus when you render wheels, you must update wheel transforms before rendering them. See raycastVehicle demo for an example. + * @method updateWheelTransform + * @param {integer} wheelIndex The wheel index to update. + */ +RaycastVehicle.prototype.updateWheelTransform = function(wheelIndex){ + var up = tmpVec4; + var right = tmpVec5; + var fwd = tmpVec6; + + var wheel = this.wheelInfos[wheelIndex]; + this.updateWheelTransformWorld(wheel); + + wheel.directionLocal.scale(-1, up); + right.copy(wheel.axleLocal); + up.cross(right, fwd); + fwd.normalize(); + right.normalize(); + + // Rotate around steering over the wheelAxle + var steering = wheel.steering; + var steeringOrn = new Quaternion(); + steeringOrn.setFromAxisAngle(up, steering); + + var rotatingOrn = new Quaternion(); + rotatingOrn.setFromAxisAngle(right, wheel.rotation); + + // World rotation of the wheel + var q = wheel.worldTransform.quaternion; + this.chassisBody.quaternion.mult(steeringOrn, q); + q.mult(rotatingOrn, q); + + q.normalize(); + + // world position of the wheel + var p = wheel.worldTransform.position; + p.copy(wheel.directionWorld); + p.scale(wheel.suspensionLength, p); + p.vadd(wheel.chassisConnectionPointWorld, p); +}; + +var directions = [ + new Vec3(1, 0, 0), + new Vec3(0, 1, 0), + new Vec3(0, 0, 1) +]; + +/** + * Get the world transform of one of the wheels + * @method getWheelTransformWorld + * @param {integer} wheelIndex + * @return {Transform} + */ +RaycastVehicle.prototype.getWheelTransformWorld = function(wheelIndex) { + return this.wheelInfos[wheelIndex].worldTransform; +}; + + +var updateFriction_surfNormalWS_scaled_proj = new Vec3(); +var updateFriction_axle = []; +var updateFriction_forwardWS = []; +var sideFrictionStiffness2 = 1; +RaycastVehicle.prototype.updateFriction = function(timeStep) { + var surfNormalWS_scaled_proj = updateFriction_surfNormalWS_scaled_proj; + + //calculate the impulse, so that the wheels don't move sidewards + var wheelInfos = this.wheelInfos; + var numWheels = wheelInfos.length; + var chassisBody = this.chassisBody; + var forwardWS = updateFriction_forwardWS; + var axle = updateFriction_axle; + + var numWheelsOnGround = 0; + + for (var i = 0; i < numWheels; i++) { + var wheel = wheelInfos[i]; + + var groundObject = wheel.raycastResult.body; + if (groundObject){ + numWheelsOnGround++; + } + + wheel.sideImpulse = 0; + wheel.forwardImpulse = 0; + if(!forwardWS[i]){ + forwardWS[i] = new Vec3(); + } + if(!axle[i]){ + axle[i] = new Vec3(); + } + } + + for (var i = 0; i < numWheels; i++){ + var wheel = wheelInfos[i]; + + var groundObject = wheel.raycastResult.body; + + if (groundObject) { + var axlei = axle[i]; + var wheelTrans = this.getWheelTransformWorld(i); + + // Get world axle + wheelTrans.vectorToWorldFrame(directions[this.indexRightAxis], axlei); + + var surfNormalWS = wheel.raycastResult.hitNormalWorld; + var proj = axlei.dot(surfNormalWS); + surfNormalWS.scale(proj, surfNormalWS_scaled_proj); + axlei.vsub(surfNormalWS_scaled_proj, axlei); + axlei.normalize(); + + surfNormalWS.cross(axlei, forwardWS[i]); + forwardWS[i].normalize(); + + wheel.sideImpulse = resolveSingleBilateral( + chassisBody, + wheel.raycastResult.hitPointWorld, + groundObject, + wheel.raycastResult.hitPointWorld, + axlei + ); + + wheel.sideImpulse *= sideFrictionStiffness2; + } + } + + var sideFactor = 1; + var fwdFactor = 0.5; + + this.sliding = false; + for (var i = 0; i < numWheels; i++) { + var wheel = wheelInfos[i]; + var groundObject = wheel.raycastResult.body; + + var rollingFriction = 0; + + wheel.slipInfo = 1; + if (groundObject) { + var defaultRollingFrictionImpulse = 0; + var maxImpulse = wheel.brake ? wheel.brake : defaultRollingFrictionImpulse; + + // btWheelContactPoint contactPt(chassisBody,groundObject,wheelInfraycastInfo.hitPointWorld,forwardWS[wheel],maxImpulse); + // rollingFriction = calcRollingFriction(contactPt); + rollingFriction = calcRollingFriction(chassisBody, groundObject, wheel.raycastResult.hitPointWorld, forwardWS[i], maxImpulse); + + rollingFriction += wheel.engineForce * timeStep; + + // rollingFriction = 0; + var factor = maxImpulse / rollingFriction; + wheel.slipInfo *= factor; + } + + //switch between active rolling (throttle), braking and non-active rolling friction (nthrottle/break) + + wheel.forwardImpulse = 0; + wheel.skidInfo = 1; + + if (groundObject) { + wheel.skidInfo = 1; + + var maximp = wheel.suspensionForce * timeStep * wheel.frictionSlip; + var maximpSide = maximp; + + var maximpSquared = maximp * maximpSide; + + wheel.forwardImpulse = rollingFriction;//wheelInfo.engineForce* timeStep; + + var x = wheel.forwardImpulse * fwdFactor; + var y = wheel.sideImpulse * sideFactor; + + var impulseSquared = x * x + y * y; + + wheel.sliding = false; + if (impulseSquared > maximpSquared) { + this.sliding = true; + wheel.sliding = true; + + var factor = maximp / Math.sqrt(impulseSquared); + + wheel.skidInfo *= factor; + } + } + } + + if (this.sliding) { + for (var i = 0; i < numWheels; i++) { + var wheel = wheelInfos[i]; + if (wheel.sideImpulse !== 0) { + if (wheel.skidInfo < 1){ + wheel.forwardImpulse *= wheel.skidInfo; + wheel.sideImpulse *= wheel.skidInfo; + } + } + } + } + + // apply the impulses + for (var i = 0; i < numWheels; i++) { + var wheel = wheelInfos[i]; + + var rel_pos = new Vec3(); + //wheel.raycastResult.hitPointWorld.vsub(chassisBody.position, rel_pos); + // cannons applyimpulse is using world coord for the position + rel_pos.copy(wheel.raycastResult.hitPointWorld); + + if (wheel.forwardImpulse !== 0) { + var impulse = new Vec3(); + forwardWS[i].scale(wheel.forwardImpulse, impulse); + chassisBody.applyImpulse(impulse, rel_pos); + } + + if (wheel.sideImpulse !== 0){ + var groundObject = wheel.raycastResult.body; + + var rel_pos2 = new Vec3(); + //wheel.raycastResult.hitPointWorld.vsub(groundObject.position, rel_pos2); + rel_pos2.copy(wheel.raycastResult.hitPointWorld); + var sideImp = new Vec3(); + axle[i].scale(wheel.sideImpulse, sideImp); + + // Scale the relative position in the up direction with rollInfluence. + // If rollInfluence is 1, the impulse will be applied on the hitPoint (easy to roll over), if it is zero it will be applied in the same plane as the center of mass (not easy to roll over). + chassisBody.pointToLocalFrame(rel_pos, rel_pos); + rel_pos['xyz'[this.indexUpAxis]] *= wheel.rollInfluence; + chassisBody.pointToWorldFrame(rel_pos, rel_pos); + chassisBody.applyImpulse(sideImp, rel_pos); + + //apply friction impulse on the ground + sideImp.scale(-1, sideImp); + groundObject.applyImpulse(sideImp, rel_pos2); + } + } +}; + +var calcRollingFriction_vel1 = new Vec3(); +var calcRollingFriction_vel2 = new Vec3(); +var calcRollingFriction_vel = new Vec3(); + +function calcRollingFriction(body0, body1, frictionPosWorld, frictionDirectionWorld, maxImpulse) { + var j1 = 0; + var contactPosWorld = frictionPosWorld; + + // var rel_pos1 = new Vec3(); + // var rel_pos2 = new Vec3(); + var vel1 = calcRollingFriction_vel1; + var vel2 = calcRollingFriction_vel2; + var vel = calcRollingFriction_vel; + // contactPosWorld.vsub(body0.position, rel_pos1); + // contactPosWorld.vsub(body1.position, rel_pos2); + + body0.getVelocityAtWorldPoint(contactPosWorld, vel1); + body1.getVelocityAtWorldPoint(contactPosWorld, vel2); + vel1.vsub(vel2, vel); + + var vrel = frictionDirectionWorld.dot(vel); + + var denom0 = computeImpulseDenominator(body0, frictionPosWorld, frictionDirectionWorld); + var denom1 = computeImpulseDenominator(body1, frictionPosWorld, frictionDirectionWorld); + var relaxation = 1; + var jacDiagABInv = relaxation / (denom0 + denom1); + + // calculate j that moves us to zero relative velocity + j1 = -vrel * jacDiagABInv; + + if (maxImpulse < j1) { + j1 = maxImpulse; + } + if (j1 < -maxImpulse) { + j1 = -maxImpulse; + } + + return j1; +} + +var computeImpulseDenominator_r0 = new Vec3(); +var computeImpulseDenominator_c0 = new Vec3(); +var computeImpulseDenominator_vec = new Vec3(); +var computeImpulseDenominator_m = new Vec3(); +function computeImpulseDenominator(body, pos, normal) { + var r0 = computeImpulseDenominator_r0; + var c0 = computeImpulseDenominator_c0; + var vec = computeImpulseDenominator_vec; + var m = computeImpulseDenominator_m; + + pos.vsub(body.position, r0); + r0.cross(normal, c0); + body.invInertiaWorld.vmult(c0, m); + m.cross(r0, vec); + + return body.invMass + normal.dot(vec); +} + + +var resolveSingleBilateral_vel1 = new Vec3(); +var resolveSingleBilateral_vel2 = new Vec3(); +var resolveSingleBilateral_vel = new Vec3(); + +//bilateral constraint between two dynamic objects +function resolveSingleBilateral(body1, pos1, body2, pos2, normal, impulse){ + var normalLenSqr = normal.norm2(); + if (normalLenSqr > 1.1){ + return 0; // no impulse + } + // var rel_pos1 = new Vec3(); + // var rel_pos2 = new Vec3(); + // pos1.vsub(body1.position, rel_pos1); + // pos2.vsub(body2.position, rel_pos2); + + var vel1 = resolveSingleBilateral_vel1; + var vel2 = resolveSingleBilateral_vel2; + var vel = resolveSingleBilateral_vel; + body1.getVelocityAtWorldPoint(pos1, vel1); + body2.getVelocityAtWorldPoint(pos2, vel2); + + vel1.vsub(vel2, vel); + + var rel_vel = normal.dot(vel); + + var contactDamping = 0.2; + var massTerm = 1 / (body1.invMass + body2.invMass); + var impulse = - contactDamping * rel_vel * massTerm; + + return impulse; +} +},{"../collision/Ray":9,"../collision/RaycastResult":10,"../math/Quaternion":28,"../math/Vec3":30,"../objects/WheelInfo":36,"./Body":31}],33:[function(_dereq_,module,exports){ +var Body = _dereq_('./Body'); +var Sphere = _dereq_('../shapes/Sphere'); +var Box = _dereq_('../shapes/Box'); +var Vec3 = _dereq_('../math/Vec3'); +var HingeConstraint = _dereq_('../constraints/HingeConstraint'); + +module.exports = RigidVehicle; + +/** + * Simple vehicle helper class with spherical rigid body wheels. + * @class RigidVehicle + * @constructor + * @param {Body} [options.chassisBody] + */ +function RigidVehicle(options){ + this.wheelBodies = []; + + /** + * @property coordinateSystem + * @type {Vec3} + */ + this.coordinateSystem = typeof(options.coordinateSystem)==='undefined' ? new Vec3(1, 2, 3) : options.coordinateSystem.clone(); + + /** + * @property {Body} chassisBody + */ + this.chassisBody = options.chassisBody; + + if(!this.chassisBody){ + // No chassis body given. Create it! + var chassisShape = new Box(new Vec3(5, 2, 0.5)); + this.chassisBody = new Body(1, chassisShape); + } + + /** + * @property constraints + * @type {Array} + */ + this.constraints = []; + + this.wheelAxes = []; + this.wheelForces = []; +} + +/** + * Add a wheel + * @method addWheel + * @param {object} options + * @param {boolean} [options.isFrontWheel] + * @param {Vec3} [options.position] Position of the wheel, locally in the chassis body. + * @param {Vec3} [options.direction] Slide direction of the wheel along the suspension. + * @param {Vec3} [options.axis] Axis of rotation of the wheel, locally defined in the chassis. + * @param {Body} [options.body] The wheel body. + */ +RigidVehicle.prototype.addWheel = function(options){ + options = options || {}; + var wheelBody = options.body; + if(!wheelBody){ + wheelBody = new Body(1, new Sphere(1.2)); + } + this.wheelBodies.push(wheelBody); + this.wheelForces.push(0); + + // Position constrain wheels + var zero = new Vec3(); + var position = typeof(options.position) !== 'undefined' ? options.position.clone() : new Vec3(); + + // Set position locally to the chassis + var worldPosition = new Vec3(); + this.chassisBody.pointToWorldFrame(position, worldPosition); + wheelBody.position.set(worldPosition.x, worldPosition.y, worldPosition.z); + + // Constrain wheel + var axis = typeof(options.axis) !== 'undefined' ? options.axis.clone() : new Vec3(0, 1, 0); + this.wheelAxes.push(axis); + + var hingeConstraint = new HingeConstraint(this.chassisBody, wheelBody, { + pivotA: position, + axisA: axis, + pivotB: Vec3.ZERO, + axisB: axis, + collideConnected: false + }); + this.constraints.push(hingeConstraint); + + return this.wheelBodies.length - 1; +}; + +/** + * Set the steering value of a wheel. + * @method setSteeringValue + * @param {number} value + * @param {integer} wheelIndex + * @todo check coordinateSystem + */ +RigidVehicle.prototype.setSteeringValue = function(value, wheelIndex){ + // Set angle of the hinge axis + var axis = this.wheelAxes[wheelIndex]; + + var c = Math.cos(value), + s = Math.sin(value), + x = axis.x, + y = axis.y; + this.constraints[wheelIndex].axisA.set( + c*x -s*y, + s*x +c*y, + 0 + ); +}; + +/** + * Set the target rotational speed of the hinge constraint. + * @method setMotorSpeed + * @param {number} value + * @param {integer} wheelIndex + */ +RigidVehicle.prototype.setMotorSpeed = function(value, wheelIndex){ + var hingeConstraint = this.constraints[wheelIndex]; + hingeConstraint.enableMotor(); + hingeConstraint.motorTargetVelocity = value; +}; + +/** + * Set the target rotational speed of the hinge constraint. + * @method disableMotor + * @param {number} value + * @param {integer} wheelIndex + */ +RigidVehicle.prototype.disableMotor = function(wheelIndex){ + var hingeConstraint = this.constraints[wheelIndex]; + hingeConstraint.disableMotor(); +}; + +var torque = new Vec3(); + +/** + * Set the wheel force to apply on one of the wheels each time step + * @method setWheelForce + * @param {number} value + * @param {integer} wheelIndex + */ +RigidVehicle.prototype.setWheelForce = function(value, wheelIndex){ + this.wheelForces[wheelIndex] = value; +}; + +/** + * Apply a torque on one of the wheels. + * @method applyWheelForce + * @param {number} value + * @param {integer} wheelIndex + */ +RigidVehicle.prototype.applyWheelForce = function(value, wheelIndex){ + var axis = this.wheelAxes[wheelIndex]; + var wheelBody = this.wheelBodies[wheelIndex]; + var bodyTorque = wheelBody.torque; + + axis.scale(value, torque); + wheelBody.vectorToWorldFrame(torque, torque); + bodyTorque.vadd(torque, bodyTorque); +}; + +/** + * Add the vehicle including its constraints to the world. + * @method addToWorld + * @param {World} world + */ +RigidVehicle.prototype.addToWorld = function(world){ + var constraints = this.constraints; + var bodies = this.wheelBodies.concat([this.chassisBody]); + + for (var i = 0; i < bodies.length; i++) { + world.add(bodies[i]); + } + + for (var i = 0; i < constraints.length; i++) { + world.addConstraint(constraints[i]); + } + + world.addEventListener('preStep', this._update.bind(this)); +}; + +RigidVehicle.prototype._update = function(){ + var wheelForces = this.wheelForces; + for (var i = 0; i < wheelForces.length; i++) { + this.applyWheelForce(wheelForces[i], i); + } +}; + +/** + * Remove the vehicle including its constraints from the world. + * @method removeFromWorld + * @param {World} world + */ +RigidVehicle.prototype.removeFromWorld = function(world){ + var constraints = this.constraints; + var bodies = this.wheelBodies.concat([this.chassisBody]); + + for (var i = 0; i < bodies.length; i++) { + world.remove(bodies[i]); + } + + for (var i = 0; i < constraints.length; i++) { + world.removeConstraint(constraints[i]); + } +}; + +var worldAxis = new Vec3(); + +/** + * Get current rotational velocity of a wheel + * @method getWheelSpeed + * @param {integer} wheelIndex + */ +RigidVehicle.prototype.getWheelSpeed = function(wheelIndex){ + var axis = this.wheelAxes[wheelIndex]; + var wheelBody = this.wheelBodies[wheelIndex]; + var w = wheelBody.angularVelocity; + this.chassisBody.vectorToWorldFrame(axis, worldAxis); + return w.dot(worldAxis); +}; + +},{"../constraints/HingeConstraint":15,"../math/Vec3":30,"../shapes/Box":37,"../shapes/Sphere":44,"./Body":31}],34:[function(_dereq_,module,exports){ +module.exports = SPHSystem; + +var Shape = _dereq_('../shapes/Shape'); +var Vec3 = _dereq_('../math/Vec3'); +var Quaternion = _dereq_('../math/Quaternion'); +var Particle = _dereq_('../shapes/Particle'); +var Body = _dereq_('../objects/Body'); +var Material = _dereq_('../material/Material'); + +/** + * Smoothed-particle hydrodynamics system + * @class SPHSystem + * @constructor + */ +function SPHSystem(){ + this.particles = []; + + /** + * Density of the system (kg/m3). + * @property {number} density + */ + this.density = 1; + + /** + * Distance below which two particles are considered to be neighbors. + * It should be adjusted so there are about 15-20 neighbor particles within this radius. + * @property {number} smoothingRadius + */ + this.smoothingRadius = 1; + this.speedOfSound = 1; + + /** + * Viscosity of the system. + * @property {number} viscosity + */ + this.viscosity = 0.01; + this.eps = 0.000001; + + // Stuff Computed per particle + this.pressures = []; + this.densities = []; + this.neighbors = []; +} + +/** + * Add a particle to the system. + * @method add + * @param {Body} particle + */ +SPHSystem.prototype.add = function(particle){ + this.particles.push(particle); + if(this.neighbors.length < this.particles.length){ + this.neighbors.push([]); + } +}; + +/** + * Remove a particle from the system. + * @method remove + * @param {Body} particle + */ +SPHSystem.prototype.remove = function(particle){ + var idx = this.particles.indexOf(particle); + if(idx !== -1){ + this.particles.splice(idx,1); + if(this.neighbors.length > this.particles.length){ + this.neighbors.pop(); + } + } +}; + +/** + * Get neighbors within smoothing volume, save in the array neighbors + * @method getNeighbors + * @param {Body} particle + * @param {Array} neighbors + */ +var SPHSystem_getNeighbors_dist = new Vec3(); +SPHSystem.prototype.getNeighbors = function(particle,neighbors){ + var N = this.particles.length, + id = particle.id, + R2 = this.smoothingRadius * this.smoothingRadius, + dist = SPHSystem_getNeighbors_dist; + for(var i=0; i!==N; i++){ + var p = this.particles[i]; + p.position.vsub(particle.position,dist); + if(id!==p.id && dist.norm2() < R2){ + neighbors.push(p); + } + } +}; + +// Temp vectors for calculation +var SPHSystem_update_dist = new Vec3(), + SPHSystem_update_a_pressure = new Vec3(), + SPHSystem_update_a_visc = new Vec3(), + SPHSystem_update_gradW = new Vec3(), + SPHSystem_update_r_vec = new Vec3(), + SPHSystem_update_u = new Vec3(); // Relative velocity +SPHSystem.prototype.update = function(){ + var N = this.particles.length, + dist = SPHSystem_update_dist, + cs = this.speedOfSound, + eps = this.eps; + + for(var i=0; i!==N; i++){ + var p = this.particles[i]; // Current particle + var neighbors = this.neighbors[i]; + + // Get neighbors + neighbors.length = 0; + this.getNeighbors(p,neighbors); + neighbors.push(this.particles[i]); // Add current too + var numNeighbors = neighbors.length; + + // Accumulate density for the particle + var sum = 0.0; + for(var j=0; j!==numNeighbors; j++){ + + //printf("Current particle has position %f %f %f\n",objects[id].pos.x(),objects[id].pos.y(),objects[id].pos.z()); + p.position.vsub(neighbors[j].position, dist); + var len = dist.norm(); + + var weight = this.w(len); + sum += neighbors[j].mass * weight; + } + + // Save + this.densities[i] = sum; + this.pressures[i] = cs * cs * (this.densities[i] - this.density); + } + + // Add forces + + // Sum to these accelerations + var a_pressure= SPHSystem_update_a_pressure; + var a_visc = SPHSystem_update_a_visc; + var gradW = SPHSystem_update_gradW; + var r_vec = SPHSystem_update_r_vec; + var u = SPHSystem_update_u; + + for(var i=0; i!==N; i++){ + + var particle = this.particles[i]; + + a_pressure.set(0,0,0); + a_visc.set(0,0,0); + + // Init vars + var Pij; + var nabla; + var Vij; + + // Sum up for all other neighbors + var neighbors = this.neighbors[i]; + var numNeighbors = neighbors.length; + + //printf("Neighbors: "); + for(var j=0; j!==numNeighbors; j++){ + + var neighbor = neighbors[j]; + //printf("%d ",nj); + + // Get r once for all.. + particle.position.vsub(neighbor.position,r_vec); + var r = r_vec.norm(); + + // Pressure contribution + Pij = -neighbor.mass * (this.pressures[i] / (this.densities[i]*this.densities[i] + eps) + this.pressures[j] / (this.densities[j]*this.densities[j] + eps)); + this.gradw(r_vec, gradW); + // Add to pressure acceleration + gradW.mult(Pij , gradW); + a_pressure.vadd(gradW, a_pressure); + + // Viscosity contribution + neighbor.velocity.vsub(particle.velocity, u); + u.mult( 1.0 / (0.0001+this.densities[i] * this.densities[j]) * this.viscosity * neighbor.mass , u ); + nabla = this.nablaw(r); + u.mult(nabla,u); + // Add to viscosity acceleration + a_visc.vadd( u, a_visc ); + } + + // Calculate force + a_visc.mult(particle.mass, a_visc); + a_pressure.mult(particle.mass, a_pressure); + + // Add force to particles + particle.force.vadd(a_visc, particle.force); + particle.force.vadd(a_pressure, particle.force); + } +}; + +// Calculate the weight using the W(r) weightfunction +SPHSystem.prototype.w = function(r){ + // 315 + var h = this.smoothingRadius; + return 315.0/(64.0*Math.PI*Math.pow(h,9)) * Math.pow(h*h-r*r,3); +}; + +// calculate gradient of the weight function +SPHSystem.prototype.gradw = function(rVec,resultVec){ + var r = rVec.norm(), + h = this.smoothingRadius; + rVec.mult(945.0/(32.0*Math.PI*Math.pow(h,9)) * Math.pow((h*h-r*r),2) , resultVec); +}; + +// Calculate nabla(W) +SPHSystem.prototype.nablaw = function(r){ + var h = this.smoothingRadius; + var nabla = 945.0/(32.0*Math.PI*Math.pow(h,9)) * (h*h-r*r)*(7*r*r - 3*h*h); + return nabla; +}; + +},{"../material/Material":25,"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"../shapes/Particle":41,"../shapes/Shape":43}],35:[function(_dereq_,module,exports){ +var Vec3 = _dereq_('../math/Vec3'); + +module.exports = Spring; + +/** + * A spring, connecting two bodies. + * + * @class Spring + * @constructor + * @param {Body} bodyA + * @param {Body} bodyB + * @param {Object} [options] + * @param {number} [options.restLength] A number > 0. Default: 1 + * @param {number} [options.stiffness] A number >= 0. Default: 100 + * @param {number} [options.damping] A number >= 0. Default: 1 + * @param {Vec3} [options.worldAnchorA] Where to hook the spring to body A, in world coordinates. + * @param {Vec3} [options.worldAnchorB] + * @param {Vec3} [options.localAnchorA] Where to hook the spring to body A, in local body coordinates. + * @param {Vec3} [options.localAnchorB] + */ +function Spring(bodyA,bodyB,options){ + options = options || {}; + + /** + * Rest length of the spring. + * @property restLength + * @type {number} + */ + this.restLength = typeof(options.restLength) === "number" ? options.restLength : 1; + + /** + * Stiffness of the spring. + * @property stiffness + * @type {number} + */ + this.stiffness = options.stiffness || 100; + + /** + * Damping of the spring. + * @property damping + * @type {number} + */ + this.damping = options.damping || 1; + + /** + * First connected body. + * @property bodyA + * @type {Body} + */ + this.bodyA = bodyA; + + /** + * Second connected body. + * @property bodyB + * @type {Body} + */ + this.bodyB = bodyB; + + /** + * Anchor for bodyA in local bodyA coordinates. + * @property localAnchorA + * @type {Vec3} + */ + this.localAnchorA = new Vec3(); + + /** + * Anchor for bodyB in local bodyB coordinates. + * @property localAnchorB + * @type {Vec3} + */ + this.localAnchorB = new Vec3(); + + if(options.localAnchorA){ + this.localAnchorA.copy(options.localAnchorA); + } + if(options.localAnchorB){ + this.localAnchorB.copy(options.localAnchorB); + } + if(options.worldAnchorA){ + this.setWorldAnchorA(options.worldAnchorA); + } + if(options.worldAnchorB){ + this.setWorldAnchorB(options.worldAnchorB); + } +} + +/** + * Set the anchor point on body A, using world coordinates. + * @method setWorldAnchorA + * @param {Vec3} worldAnchorA + */ +Spring.prototype.setWorldAnchorA = function(worldAnchorA){ + this.bodyA.pointToLocalFrame(worldAnchorA,this.localAnchorA); +}; + +/** + * Set the anchor point on body B, using world coordinates. + * @method setWorldAnchorB + * @param {Vec3} worldAnchorB + */ +Spring.prototype.setWorldAnchorB = function(worldAnchorB){ + this.bodyB.pointToLocalFrame(worldAnchorB,this.localAnchorB); +}; + +/** + * Get the anchor point on body A, in world coordinates. + * @method getWorldAnchorA + * @param {Vec3} result The vector to store the result in. + */ +Spring.prototype.getWorldAnchorA = function(result){ + this.bodyA.pointToWorldFrame(this.localAnchorA,result); +}; + +/** + * Get the anchor point on body B, in world coordinates. + * @method getWorldAnchorB + * @param {Vec3} result The vector to store the result in. + */ +Spring.prototype.getWorldAnchorB = function(result){ + this.bodyB.pointToWorldFrame(this.localAnchorB,result); +}; + +var applyForce_r = new Vec3(), + applyForce_r_unit = new Vec3(), + applyForce_u = new Vec3(), + applyForce_f = new Vec3(), + applyForce_worldAnchorA = new Vec3(), + applyForce_worldAnchorB = new Vec3(), + applyForce_ri = new Vec3(), + applyForce_rj = new Vec3(), + applyForce_ri_x_f = new Vec3(), + applyForce_rj_x_f = new Vec3(), + applyForce_tmp = new Vec3(); + +/** + * Apply the spring force to the connected bodies. + * @method applyForce + */ +Spring.prototype.applyForce = function(){ + var k = this.stiffness, + d = this.damping, + l = this.restLength, + bodyA = this.bodyA, + bodyB = this.bodyB, + r = applyForce_r, + r_unit = applyForce_r_unit, + u = applyForce_u, + f = applyForce_f, + tmp = applyForce_tmp; + + var worldAnchorA = applyForce_worldAnchorA, + worldAnchorB = applyForce_worldAnchorB, + ri = applyForce_ri, + rj = applyForce_rj, + ri_x_f = applyForce_ri_x_f, + rj_x_f = applyForce_rj_x_f; + + // Get world anchors + this.getWorldAnchorA(worldAnchorA); + this.getWorldAnchorB(worldAnchorB); + + // Get offset points + worldAnchorA.vsub(bodyA.position,ri); + worldAnchorB.vsub(bodyB.position,rj); + + // Compute distance vector between world anchor points + worldAnchorB.vsub(worldAnchorA,r); + var rlen = r.norm(); + r_unit.copy(r); + r_unit.normalize(); + + // Compute relative velocity of the anchor points, u + bodyB.velocity.vsub(bodyA.velocity,u); + // Add rotational velocity + + bodyB.angularVelocity.cross(rj,tmp); + u.vadd(tmp,u); + bodyA.angularVelocity.cross(ri,tmp); + u.vsub(tmp,u); + + // F = - k * ( x - L ) - D * ( u ) + r_unit.mult(-k*(rlen-l) - d*u.dot(r_unit), f); + + // Add forces to bodies + bodyA.force.vsub(f,bodyA.force); + bodyB.force.vadd(f,bodyB.force); + + // Angular force + ri.cross(f,ri_x_f); + rj.cross(f,rj_x_f); + bodyA.torque.vsub(ri_x_f,bodyA.torque); + bodyB.torque.vadd(rj_x_f,bodyB.torque); +}; + +},{"../math/Vec3":30}],36:[function(_dereq_,module,exports){ +var Vec3 = _dereq_('../math/Vec3'); +var Transform = _dereq_('../math/Transform'); +var RaycastResult = _dereq_('../collision/RaycastResult'); +var Utils = _dereq_('../utils/Utils'); + +module.exports = WheelInfo; + +/** + * @class WheelInfo + * @constructor + * @param {Object} [options] + * + * @param {Vec3} [options.chassisConnectionPointLocal] + * @param {Vec3} [options.chassisConnectionPointWorld] + * @param {Vec3} [options.directionLocal] + * @param {Vec3} [options.directionWorld] + * @param {Vec3} [options.axleLocal] + * @param {Vec3} [options.axleWorld] + * @param {number} [options.suspensionRestLength=1] + * @param {number} [options.suspensionMaxLength=2] + * @param {number} [options.radius=1] + * @param {number} [options.suspensionStiffness=100] + * @param {number} [options.dampingCompression=10] + * @param {number} [options.dampingRelaxation=10] + * @param {number} [options.frictionSlip=10000] + * @param {number} [options.steering=0] + * @param {number} [options.rotation=0] + * @param {number} [options.deltaRotation=0] + * @param {number} [options.rollInfluence=0.01] + * @param {number} [options.maxSuspensionForce] + * @param {boolean} [options.isFrontWheel=true] + * @param {number} [options.clippedInvContactDotSuspension=1] + * @param {number} [options.suspensionRelativeVelocity=0] + * @param {number} [options.suspensionForce=0] + * @param {number} [options.skidInfo=0] + * @param {number} [options.suspensionLength=0] + * @param {number} [options.maxSuspensionTravel=1] + * @param {boolean} [options.useCustomSlidingRotationalSpeed=false] + * @param {number} [options.customSlidingRotationalSpeed=-0.1] + */ +function WheelInfo(options){ + options = Utils.defaults(options, { + chassisConnectionPointLocal: new Vec3(), + chassisConnectionPointWorld: new Vec3(), + directionLocal: new Vec3(), + directionWorld: new Vec3(), + axleLocal: new Vec3(), + axleWorld: new Vec3(), + suspensionRestLength: 1, + suspensionMaxLength: 2, + radius: 1, + suspensionStiffness: 100, + dampingCompression: 10, + dampingRelaxation: 10, + frictionSlip: 10000, + steering: 0, + rotation: 0, + deltaRotation: 0, + rollInfluence: 0.01, + maxSuspensionForce: Number.MAX_VALUE, + isFrontWheel: true, + clippedInvContactDotSuspension: 1, + suspensionRelativeVelocity: 0, + suspensionForce: 0, + skidInfo: 0, + suspensionLength: 0, + maxSuspensionTravel: 1, + useCustomSlidingRotationalSpeed: false, + customSlidingRotationalSpeed: -0.1 + }); + + /** + * Max travel distance of the suspension, in meters. + * @property {number} maxSuspensionTravel + */ + this.maxSuspensionTravel = options.maxSuspensionTravel; + + /** + * Speed to apply to the wheel rotation when the wheel is sliding. + * @property {number} customSlidingRotationalSpeed + */ + this.customSlidingRotationalSpeed = options.customSlidingRotationalSpeed; + + /** + * If the customSlidingRotationalSpeed should be used. + * @property {Boolean} useCustomSlidingRotationalSpeed + */ + this.useCustomSlidingRotationalSpeed = options.useCustomSlidingRotationalSpeed; + + /** + * @property {Boolean} sliding + */ + this.sliding = false; + + /** + * Connection point, defined locally in the chassis body frame. + * @property {Vec3} chassisConnectionPointLocal + */ + this.chassisConnectionPointLocal = options.chassisConnectionPointLocal.clone(); + + /** + * @property {Vec3} chassisConnectionPointWorld + */ + this.chassisConnectionPointWorld = options.chassisConnectionPointWorld.clone(); + + /** + * @property {Vec3} directionLocal + */ + this.directionLocal = options.directionLocal.clone(); + + /** + * @property {Vec3} directionWorld + */ + this.directionWorld = options.directionWorld.clone(); + + /** + * @property {Vec3} axleLocal + */ + this.axleLocal = options.axleLocal.clone(); + + /** + * @property {Vec3} axleWorld + */ + this.axleWorld = options.axleWorld.clone(); + + /** + * @property {number} suspensionRestLength + */ + this.suspensionRestLength = options.suspensionRestLength; + + /** + * @property {number} suspensionMaxLength + */ + this.suspensionMaxLength = options.suspensionMaxLength; + + /** + * @property {number} radius + */ + this.radius = options.radius; + + /** + * @property {number} suspensionStiffness + */ + this.suspensionStiffness = options.suspensionStiffness; + + /** + * @property {number} dampingCompression + */ + this.dampingCompression = options.dampingCompression; + + /** + * @property {number} dampingRelaxation + */ + this.dampingRelaxation = options.dampingRelaxation; + + /** + * @property {number} frictionSlip + */ + this.frictionSlip = options.frictionSlip; + + /** + * @property {number} steering + */ + this.steering = 0; + + /** + * Rotation value, in radians. + * @property {number} rotation + */ + this.rotation = 0; + + /** + * @property {number} deltaRotation + */ + this.deltaRotation = 0; + + /** + * @property {number} rollInfluence + */ + this.rollInfluence = options.rollInfluence; + + /** + * @property {number} maxSuspensionForce + */ + this.maxSuspensionForce = options.maxSuspensionForce; + + /** + * @property {number} engineForce + */ + this.engineForce = 0; + + /** + * @property {number} brake + */ + this.brake = 0; + + /** + * @property {number} isFrontWheel + */ + this.isFrontWheel = options.isFrontWheel; + + /** + * @property {number} clippedInvContactDotSuspension + */ + this.clippedInvContactDotSuspension = 1; + + /** + * @property {number} suspensionRelativeVelocity + */ + this.suspensionRelativeVelocity = 0; + + /** + * @property {number} suspensionForce + */ + this.suspensionForce = 0; + + /** + * @property {number} skidInfo + */ + this.skidInfo = 0; + + /** + * @property {number} suspensionLength + */ + this.suspensionLength = 0; + + /** + * @property {number} sideImpulse + */ + this.sideImpulse = 0; + + /** + * @property {number} forwardImpulse + */ + this.forwardImpulse = 0; + + /** + * The result from raycasting + * @property {RaycastResult} raycastResult + */ + this.raycastResult = new RaycastResult(); + + /** + * Wheel world transform + * @property {Transform} worldTransform + */ + this.worldTransform = new Transform(); + + /** + * @property {boolean} isInContact + */ + this.isInContact = false; +} + +var chassis_velocity_at_contactPoint = new Vec3(); +var relpos = new Vec3(); +var chassis_velocity_at_contactPoint = new Vec3(); +WheelInfo.prototype.updateWheel = function(chassis){ + var raycastResult = this.raycastResult; + + if (this.isInContact){ + var project= raycastResult.hitNormalWorld.dot(raycastResult.directionWorld); + raycastResult.hitPointWorld.vsub(chassis.position, relpos); + chassis.getVelocityAtWorldPoint(relpos, chassis_velocity_at_contactPoint); + var projVel = raycastResult.hitNormalWorld.dot( chassis_velocity_at_contactPoint ); + if (project >= -0.1) { + this.suspensionRelativeVelocity = 0.0; + this.clippedInvContactDotSuspension = 1.0 / 0.1; + } else { + var inv = -1 / project; + this.suspensionRelativeVelocity = projVel * inv; + this.clippedInvContactDotSuspension = inv; + } + + } else { + // Not in contact : position wheel in a nice (rest length) position + raycastResult.suspensionLength = this.suspensionRestLength; + this.suspensionRelativeVelocity = 0.0; + raycastResult.directionWorld.scale(-1, raycastResult.hitNormalWorld); + this.clippedInvContactDotSuspension = 1.0; + } +}; +},{"../collision/RaycastResult":10,"../math/Transform":29,"../math/Vec3":30,"../utils/Utils":53}],37:[function(_dereq_,module,exports){ +module.exports = Box; + +var Shape = _dereq_('./Shape'); +var Vec3 = _dereq_('../math/Vec3'); +var ConvexPolyhedron = _dereq_('./ConvexPolyhedron'); + +/** + * A 3d box shape. + * @class Box + * @constructor + * @param {Vec3} halfExtents + * @author schteppe + * @extends Shape + */ +function Box(halfExtents){ + Shape.call(this); + + this.type = Shape.types.BOX; + + /** + * @property halfExtents + * @type {Vec3} + */ + this.halfExtents = halfExtents; + + /** + * Used by the contact generator to make contacts with other convex polyhedra for example + * @property convexPolyhedronRepresentation + * @type {ConvexPolyhedron} + */ + this.convexPolyhedronRepresentation = null; + + this.updateConvexPolyhedronRepresentation(); + this.updateBoundingSphereRadius(); +} +Box.prototype = new Shape(); +Box.prototype.constructor = Box; + +/** + * Updates the local convex polyhedron representation used for some collisions. + * @method updateConvexPolyhedronRepresentation + */ +Box.prototype.updateConvexPolyhedronRepresentation = function(){ + var sx = this.halfExtents.x; + var sy = this.halfExtents.y; + var sz = this.halfExtents.z; + var V = Vec3; + + var vertices = [ + new V(-sx,-sy,-sz), + new V( sx,-sy,-sz), + new V( sx, sy,-sz), + new V(-sx, sy,-sz), + new V(-sx,-sy, sz), + new V( sx,-sy, sz), + new V( sx, sy, sz), + new V(-sx, sy, sz) + ]; + + var indices = [ + [3,2,1,0], // -z + [4,5,6,7], // +z + [5,4,0,1], // -y + [2,3,7,6], // +y + [0,4,7,3], // -x + [1,2,6,5], // +x + ]; + + var axes = [ + new V(0, 0, 1), + new V(0, 1, 0), + new V(1, 0, 0) + ]; + + var h = new ConvexPolyhedron(vertices, indices); + this.convexPolyhedronRepresentation = h; + h.material = this.material; +}; + +/** + * @method calculateLocalInertia + * @param {Number} mass + * @param {Vec3} target + * @return {Vec3} + */ +Box.prototype.calculateLocalInertia = function(mass,target){ + target = target || new Vec3(); + Box.calculateInertia(this.halfExtents, mass, target); + return target; +}; + +Box.calculateInertia = function(halfExtents,mass,target){ + var e = halfExtents; + target.x = 1.0 / 12.0 * mass * ( 2*e.y*2*e.y + 2*e.z*2*e.z ); + target.y = 1.0 / 12.0 * mass * ( 2*e.x*2*e.x + 2*e.z*2*e.z ); + target.z = 1.0 / 12.0 * mass * ( 2*e.y*2*e.y + 2*e.x*2*e.x ); +}; + +/** + * Get the box 6 side normals + * @method getSideNormals + * @param {array} sixTargetVectors An array of 6 vectors, to store the resulting side normals in. + * @param {Quaternion} quat Orientation to apply to the normal vectors. If not provided, the vectors will be in respect to the local frame. + * @return {array} + */ +Box.prototype.getSideNormals = function(sixTargetVectors,quat){ + var sides = sixTargetVectors; + var ex = this.halfExtents; + sides[0].set( ex.x, 0, 0); + sides[1].set( 0, ex.y, 0); + sides[2].set( 0, 0, ex.z); + sides[3].set( -ex.x, 0, 0); + sides[4].set( 0, -ex.y, 0); + sides[5].set( 0, 0, -ex.z); + + if(quat!==undefined){ + for(var i=0; i!==sides.length; i++){ + quat.vmult(sides[i],sides[i]); + } + } + + return sides; +}; + +Box.prototype.volume = function(){ + return 8.0 * this.halfExtents.x * this.halfExtents.y * this.halfExtents.z; +}; + +Box.prototype.updateBoundingSphereRadius = function(){ + this.boundingSphereRadius = this.halfExtents.norm(); +}; + +var worldCornerTempPos = new Vec3(); +var worldCornerTempNeg = new Vec3(); +Box.prototype.forEachWorldCorner = function(pos,quat,callback){ + + var e = this.halfExtents; + var corners = [[ e.x, e.y, e.z], + [ -e.x, e.y, e.z], + [ -e.x, -e.y, e.z], + [ -e.x, -e.y, -e.z], + [ e.x, -e.y, -e.z], + [ e.x, e.y, -e.z], + [ -e.x, e.y, -e.z], + [ e.x, -e.y, e.z]]; + for(var i=0; i max.x){ + max.x = x; + } + if(y > max.y){ + max.y = y; + } + if(z > max.z){ + max.z = z; + } + + if(x < min.x){ + min.x = x; + } + if(y < min.y){ + min.y = y; + } + if(z < min.z){ + min.z = z; + } + } + + // Get each axis max + // min.set(Infinity,Infinity,Infinity); + // max.set(-Infinity,-Infinity,-Infinity); + // this.forEachWorldCorner(pos,quat,function(x,y,z){ + // if(x > max.x){ + // max.x = x; + // } + // if(y > max.y){ + // max.y = y; + // } + // if(z > max.z){ + // max.z = z; + // } + + // if(x < min.x){ + // min.x = x; + // } + // if(y < min.y){ + // min.y = y; + // } + // if(z < min.z){ + // min.z = z; + // } + // }); +}; + +},{"../math/Vec3":30,"./ConvexPolyhedron":38,"./Shape":43}],38:[function(_dereq_,module,exports){ +module.exports = ConvexPolyhedron; + +var Shape = _dereq_('./Shape'); +var Vec3 = _dereq_('../math/Vec3'); +var Quaternion = _dereq_('../math/Quaternion'); +var Transform = _dereq_('../math/Transform'); + +/** + * A set of polygons describing a convex shape. + * @class ConvexPolyhedron + * @constructor + * @extends Shape + * @description The shape MUST be convex for the code to work properly. No polygons may be coplanar (contained + * in the same 3D plane), instead these should be merged into one polygon. + * + * @param {array} points An array of Vec3's + * @param {array} faces Array of integer arrays, describing which vertices that is included in each face. + * + * @author qiao / https://github.com/qiao (original author, see https://github.com/qiao/three.js/commit/85026f0c769e4000148a67d45a9e9b9c5108836f) + * @author schteppe / https://github.com/schteppe + * @see http://www.altdevblogaday.com/2011/05/13/contact-generation-between-3d-convex-meshes/ + * @see http://bullet.googlecode.com/svn/trunk/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp + * + * @todo Move the clipping functions to ContactGenerator? + * @todo Automatically merge coplanar polygons in constructor. + */ +function ConvexPolyhedron(points, faces, uniqueAxes) { + var that = this; + Shape.call(this); + this.type = Shape.types.CONVEXPOLYHEDRON; + + /** + * Array of Vec3 + * @property vertices + * @type {Array} + */ + this.vertices = points||[]; + + this.worldVertices = []; // World transformed version of .vertices + this.worldVerticesNeedsUpdate = true; + + /** + * Array of integer arrays, indicating which vertices each face consists of + * @property faces + * @type {Array} + */ + this.faces = faces||[]; + + /** + * Array of Vec3 + * @property faceNormals + * @type {Array} + */ + this.faceNormals = []; + this.computeNormals(); + + this.worldFaceNormalsNeedsUpdate = true; + this.worldFaceNormals = []; // World transformed version of .faceNormals + + /** + * Array of Vec3 + * @property uniqueEdges + * @type {Array} + */ + this.uniqueEdges = []; + + /** + * If given, these locally defined, normalized axes are the only ones being checked when doing separating axis check. + * @property {Array} uniqueAxes + */ + this.uniqueAxes = uniqueAxes ? uniqueAxes.slice() : null; + + this.computeEdges(); + this.updateBoundingSphereRadius(); +} +ConvexPolyhedron.prototype = new Shape(); +ConvexPolyhedron.prototype.constructor = ConvexPolyhedron; + +var computeEdges_tmpEdge = new Vec3(); +/** + * Computes uniqueEdges + * @method computeEdges + */ +ConvexPolyhedron.prototype.computeEdges = function(){ + var faces = this.faces; + var vertices = this.vertices; + var nv = vertices.length; + var edges = this.uniqueEdges; + + edges.length = 0; + + var edge = computeEdges_tmpEdge; + + for(var i=0; i !== faces.length; i++){ + var face = faces[i]; + var numVertices = face.length; + for(var j = 0; j !== numVertices; j++){ + var k = ( j+1 ) % numVertices; + vertices[face[j]].vsub(vertices[face[k]], edge); + edge.normalize(); + var found = false; + for(var p=0; p !== edges.length; p++){ + if (edges[p].almostEquals(edge) || edges[p].almostEquals(edge)){ + found = true; + break; + } + } + + if (!found){ + edges.push(edge.clone()); + } + } + } +}; + +/** + * Compute the normals of the faces. Will reuse existing Vec3 objects in the .faceNormals array if they exist. + * @method computeNormals + */ +ConvexPolyhedron.prototype.computeNormals = function(){ + this.faceNormals.length = this.faces.length; + + // Generate normals + for(var i=0; i dmax){ + dmax = d; + closestFaceB = face; + } + } + var worldVertsB1 = []; + var polyB = hullB.faces[closestFaceB]; + var numVertices = polyB.length; + for(var e0=0; e0=0){ + this.clipFaceAgainstHull(separatingNormal, + posA, + quatA, + worldVertsB1, + minDist, + maxDist, + result); + } +}; + +/** + * Find the separating axis between this hull and another + * @method findSeparatingAxis + * @param {ConvexPolyhedron} hullB + * @param {Vec3} posA + * @param {Quaternion} quatA + * @param {Vec3} posB + * @param {Quaternion} quatB + * @param {Vec3} target The target vector to save the axis in + * @return {bool} Returns false if a separation is found, else true + */ +var fsa_faceANormalWS3 = new Vec3(), + fsa_Worldnormal1 = new Vec3(), + fsa_deltaC = new Vec3(), + fsa_worldEdge0 = new Vec3(), + fsa_worldEdge1 = new Vec3(), + fsa_Cross = new Vec3(); +ConvexPolyhedron.prototype.findSeparatingAxis = function(hullB,posA,quatA,posB,quatB,target, faceListA, faceListB){ + var faceANormalWS3 = fsa_faceANormalWS3, + Worldnormal1 = fsa_Worldnormal1, + deltaC = fsa_deltaC, + worldEdge0 = fsa_worldEdge0, + worldEdge1 = fsa_worldEdge1, + Cross = fsa_Cross; + + var dmin = Number.MAX_VALUE; + var hullA = this; + var curPlaneTests=0; + + if(!hullA.uniqueAxes){ + + var numFacesA = faceListA ? faceListA.length : hullA.faces.length; + + // Test face normals from hullA + for(var i=0; i0.0){ + target.negate(target); + } + + return true; +}; + +var maxminA=[], maxminB=[]; + +/** + * Test separating axis against two hulls. Both hulls are projected onto the axis and the overlap size is returned if there is one. + * @method testSepAxis + * @param {Vec3} axis + * @param {ConvexPolyhedron} hullB + * @param {Vec3} posA + * @param {Quaternion} quatA + * @param {Vec3} posB + * @param {Quaternion} quatB + * @return {number} The overlap depth, or FALSE if no penetration. + */ +ConvexPolyhedron.prototype.testSepAxis = function(axis, hullB, posA, quatA, posB, quatB){ + var hullA=this; + ConvexPolyhedron.project(hullA, axis, posA, quatA, maxminA); + ConvexPolyhedron.project(hullB, axis, posB, quatB, maxminB); + var maxA = maxminA[0]; + var minA = maxminA[1]; + var maxB = maxminB[0]; + var minB = maxminB[1]; + if(maxA= 0, so output intersection + var newv = new Vec3(); + firstVertex.lerp(lastVertex, + n_dot_first / (n_dot_first - n_dot_last), + newv); + outVertices.push(newv); + } + } else { + if(n_dot_last<0){ + // Start >= 0, end < 0 so output intersection and end + var newv = new Vec3(); + firstVertex.lerp(lastVertex, + n_dot_first / (n_dot_first - n_dot_last), + newv); + outVertices.push(newv); + outVertices.push(lastVertex); + } + } + firstVertex = lastVertex; + n_dot_first = n_dot_last; + } + return outVertices; +}; + +// Updates .worldVertices and sets .worldVerticesNeedsUpdate to false. +ConvexPolyhedron.prototype.computeWorldVertices = function(position,quat){ + var N = this.vertices.length; + while(this.worldVertices.length < N){ + this.worldVertices.push( new Vec3() ); + } + + var verts = this.vertices, + worldVerts = this.worldVertices; + for(var i=0; i!==N; i++){ + quat.vmult( verts[i] , worldVerts[i] ); + position.vadd( worldVerts[i] , worldVerts[i] ); + } + + this.worldVerticesNeedsUpdate = false; +}; + +var computeLocalAABB_worldVert = new Vec3(); +ConvexPolyhedron.prototype.computeLocalAABB = function(aabbmin,aabbmax){ + var n = this.vertices.length, + vertices = this.vertices, + worldVert = computeLocalAABB_worldVert; + + aabbmin.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); + aabbmax.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); + + for(var i=0; i aabbmax.x){ + aabbmax.x = v.x; + } + if (v.y < aabbmin.y){ + aabbmin.y = v.y; + } else if(v.y > aabbmax.y){ + aabbmax.y = v.y; + } + if (v.z < aabbmin.z){ + aabbmin.z = v.z; + } else if(v.z > aabbmax.z){ + aabbmax.z = v.z; + } + } +}; + +/** + * Updates .worldVertices and sets .worldVerticesNeedsUpdate to false. + * @method computeWorldFaceNormals + * @param {Quaternion} quat + */ +ConvexPolyhedron.prototype.computeWorldFaceNormals = function(quat){ + var N = this.faceNormals.length; + while(this.worldFaceNormals.length < N){ + this.worldFaceNormals.push( new Vec3() ); + } + + var normals = this.faceNormals, + worldNormals = this.worldFaceNormals; + for(var i=0; i!==N; i++){ + quat.vmult( normals[i] , worldNormals[i] ); + } + + this.worldFaceNormalsNeedsUpdate = false; +}; + +/** + * @method updateBoundingSphereRadius + */ +ConvexPolyhedron.prototype.updateBoundingSphereRadius = function(){ + // Assume points are distributed with local (0,0,0) as center + var max2 = 0; + var verts = this.vertices; + for(var i=0, N=verts.length; i!==N; i++) { + var norm2 = verts[i].norm2(); + if(norm2 > max2){ + max2 = norm2; + } + } + this.boundingSphereRadius = Math.sqrt(max2); +}; + +var tempWorldVertex = new Vec3(); + +/** + * @method calculateWorldAABB + * @param {Vec3} pos + * @param {Quaternion} quat + * @param {Vec3} min + * @param {Vec3} max + */ +ConvexPolyhedron.prototype.calculateWorldAABB = function(pos,quat,min,max){ + var n = this.vertices.length, verts = this.vertices; + var minx,miny,minz,maxx,maxy,maxz; + for(var i=0; i maxx || maxx===undefined){ + maxx = v.x; + } + + if (v.y < miny || miny===undefined){ + miny = v.y; + } else if(v.y > maxy || maxy===undefined){ + maxy = v.y; + } + + if (v.z < minz || minz===undefined){ + minz = v.z; + } else if(v.z > maxz || maxz===undefined){ + maxz = v.z; + } + } + min.set(minx,miny,minz); + max.set(maxx,maxy,maxz); +}; + +/** + * Get approximate convex volume + * @method volume + * @return {Number} + */ +ConvexPolyhedron.prototype.volume = function(){ + return 4.0 * Math.PI * this.boundingSphereRadius / 3.0; +}; + +/** + * Get an average of all the vertices positions + * @method getAveragePointLocal + * @param {Vec3} target + * @return {Vec3} + */ +ConvexPolyhedron.prototype.getAveragePointLocal = function(target){ + target = target || new Vec3(); + var n = this.vertices.length, + verts = this.vertices; + for(var i=0; i0) || (r1>0 && r2<0)){ + return false; // Encountered some other sign. Exit. + } else { + } + } + + // If we got here, all dot products were of the same sign. + return positiveResult ? 1 : -1; +}; + +/** + * Get max and min dot product of a convex hull at position (pos,quat) projected onto an axis. Results are saved in the array maxmin. + * @static + * @method project + * @param {ConvexPolyhedron} hull + * @param {Vec3} axis + * @param {Vec3} pos + * @param {Quaternion} quat + * @param {array} result result[0] and result[1] will be set to maximum and minimum, respectively. + */ +var project_worldVertex = new Vec3(); +var project_localAxis = new Vec3(); +var project_localOrigin = new Vec3(); +ConvexPolyhedron.project = function(hull, axis, pos, quat, result){ + var n = hull.vertices.length, + worldVertex = project_worldVertex, + localAxis = project_localAxis, + max = 0, + min = 0, + localOrigin = project_localOrigin, + vs = hull.vertices; + + localOrigin.setZero(); + + // Transform the axis to local + Transform.vectorToLocalFrame(pos, quat, axis, localAxis); + Transform.pointToLocalFrame(pos, quat, localOrigin, localOrigin); + var add = localOrigin.dot(localAxis); + + min = max = vs[0].dot(localAxis); + + for(var i = 1; i < n; i++){ + var val = vs[i].dot(localAxis); + + if(val > max){ + max = val; + } + + if(val < min){ + min = val; + } + } + + min -= add; + max -= add; + + if(min > max){ + // Inconsistent - swap + var temp = min; + min = max; + max = temp; + } + // Output + result[0] = max; + result[1] = min; +}; + +},{"../math/Quaternion":28,"../math/Transform":29,"../math/Vec3":30,"./Shape":43}],39:[function(_dereq_,module,exports){ +module.exports = Cylinder; + +var Shape = _dereq_('./Shape'); +var Vec3 = _dereq_('../math/Vec3'); +var Quaternion = _dereq_('../math/Quaternion'); +var ConvexPolyhedron = _dereq_('./ConvexPolyhedron'); + +/** + * @class Cylinder + * @constructor + * @extends ConvexPolyhedron + * @author schteppe / https://github.com/schteppe + * @param {Number} radiusTop + * @param {Number} radiusBottom + * @param {Number} height + * @param {Number} numSegments The number of segments to build the cylinder out of + */ +function Cylinder( radiusTop, radiusBottom, height , numSegments ) { + var N = numSegments, + verts = [], + axes = [], + faces = [], + bottomface = [], + topface = [], + cos = Math.cos, + sin = Math.sin; + + // First bottom point + verts.push(new Vec3(radiusBottom*cos(0), + radiusBottom*sin(0), + -height*0.5)); + bottomface.push(0); + + // First top point + verts.push(new Vec3(radiusTop*cos(0), + radiusTop*sin(0), + height*0.5)); + topface.push(1); + + for(var i=0; i { convex: ..., offset: ... } + // for example: + // _cachedPillars["0_2_1"] + this._cachedPillars = {}; +} +Heightfield.prototype = new Shape(); + +/** + * Call whenever you change the data array. + * @method update + */ +Heightfield.prototype.update = function(){ + this._cachedPillars = {}; +}; + +/** + * Update the .minValue property + * @method updateMinValue + */ +Heightfield.prototype.updateMinValue = function(){ + var data = this.data; + var minValue = data[0][0]; + for(var i=0; i !== data.length; i++){ + for(var j=0; j !== data[i].length; j++){ + var v = data[i][j]; + if(v < minValue){ + minValue = v; + } + } + } + this.minValue = minValue; +}; + +/** + * Update the .maxValue property + * @method updateMaxValue + */ +Heightfield.prototype.updateMaxValue = function(){ + var data = this.data; + var maxValue = data[0][0]; + for(var i=0; i !== data.length; i++){ + for(var j=0; j !== data[i].length; j++){ + var v = data[i][j]; + if(v > maxValue){ + maxValue = v; + } + } + } + this.maxValue = maxValue; +}; + +/** + * Set the height value at an index. Don't forget to update maxValue and minValue after you're done. + * @method setHeightValueAtIndex + * @param {integer} xi + * @param {integer} yi + * @param {number} value + */ +Heightfield.prototype.setHeightValueAtIndex = function(xi, yi, value){ + var data = this.data; + data[xi][yi] = value; + + // Invalidate cache + this.clearCachedConvexTrianglePillar(xi, yi, false); + if(xi > 0){ + this.clearCachedConvexTrianglePillar(xi - 1, yi, true); + this.clearCachedConvexTrianglePillar(xi - 1, yi, false); + } + if(yi > 0){ + this.clearCachedConvexTrianglePillar(xi, yi - 1, true); + this.clearCachedConvexTrianglePillar(xi, yi - 1, false); + } + if(yi > 0 && xi > 0){ + this.clearCachedConvexTrianglePillar(xi - 1, yi - 1, true); + } +}; + +/** + * Get max/min in a rectangle in the matrix data + * @method getRectMinMax + * @param {integer} iMinX + * @param {integer} iMinY + * @param {integer} iMaxX + * @param {integer} iMaxY + * @param {array} [result] An array to store the results in. + * @return {array} The result array, if it was passed in. Minimum will be at position 0 and max at 1. + */ +Heightfield.prototype.getRectMinMax = function (iMinX, iMinY, iMaxX, iMaxY, result) { + result = result || []; + + // Get max and min of the data + var data = this.data, + max = this.minValue; // Set first value + for(var i = iMinX; i <= iMaxX; i++){ + for(var j = iMinY; j <= iMaxY; j++){ + var height = data[i][j]; + if(height > max){ + max = height; + } + } + } + + result[0] = this.minValue; + result[1] = max; +}; + +/** + * Get the index of a local position on the heightfield. The indexes indicate the rectangles, so if your terrain is made of N x N height data points, you will have rectangle indexes ranging from 0 to N-1. + * @method getIndexOfPosition + * @param {number} x + * @param {number} y + * @param {array} result Two-element array + * @param {boolean} clamp If the position should be clamped to the heightfield edge. + * @return {boolean} + */ +Heightfield.prototype.getIndexOfPosition = function (x, y, result, clamp) { + + // Get the index of the data points to test against + var w = this.elementSize; + var data = this.data; + var xi = Math.floor(x / w); + var yi = Math.floor(y / w); + + result[0] = xi; + result[1] = yi; + + if(clamp){ + // Clamp index to edges + if(xi < 0){ xi = 0; } + if(yi < 0){ yi = 0; } + if(xi >= data.length - 1){ xi = data.length - 1; } + if(yi >= data[0].length - 1){ yi = data[0].length - 1; } + } + + // Bail out if we are out of the terrain + if(xi < 0 || yi < 0 || xi >= data.length-1 || yi >= data[0].length-1){ + return false; + } + + return true; +}; + +Heightfield.prototype.getHeightAt = function(x, y, edgeClamp){ + var idx = []; + this.getIndexOfPosition(x, y, idx, edgeClamp); + + // TODO: get upper or lower triangle, then use barycentric interpolation to get the height in the triangle. + var minmax = []; + this.getRectMinMax(idx[0], idx[1] + 1, idx[0], idx[1] + 1, minmax); + + return (minmax[0] + minmax[1]) / 2; // average +}; + +Heightfield.prototype.getCacheConvexTrianglePillarKey = function(xi, yi, getUpperTriangle){ + return xi + '_' + yi + '_' + (getUpperTriangle ? 1 : 0); +}; + +Heightfield.prototype.getCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle){ + return this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)]; +}; + +Heightfield.prototype.setCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle, convex, offset){ + this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)] = { + convex: convex, + offset: offset + }; +}; + +Heightfield.prototype.clearCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle){ + delete this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)]; +}; + +/** + * Get a triangle in the terrain in the form of a triangular convex shape. + * @method getConvexTrianglePillar + * @param {integer} i + * @param {integer} j + * @param {boolean} getUpperTriangle + */ +Heightfield.prototype.getConvexTrianglePillar = function(xi, yi, getUpperTriangle){ + var result = this.pillarConvex; + var offsetResult = this.pillarOffset; + + if(this.cacheEnabled){ + var data = this.getCachedConvexTrianglePillar(xi, yi, getUpperTriangle); + if(data){ + this.pillarConvex = data.convex; + this.pillarOffset = data.offset; + return; + } + + result = new ConvexPolyhedron(); + offsetResult = new Vec3(); + + this.pillarConvex = result; + this.pillarOffset = offsetResult; + } + + var data = this.data; + var elementSize = this.elementSize; + var faces = result.faces; + + // Reuse verts if possible + result.vertices.length = 6; + for (var i = 0; i < 6; i++) { + if(!result.vertices[i]){ + result.vertices[i] = new Vec3(); + } + } + + // Reuse faces if possible + faces.length = 5; + for (var i = 0; i < 5; i++) { + if(!faces[i]){ + faces[i] = []; + } + } + + var verts = result.vertices; + + var h = (Math.min( + data[xi][yi], + data[xi+1][yi], + data[xi][yi+1], + data[xi+1][yi+1] + ) - this.minValue ) / 2 + this.minValue; + + if (!getUpperTriangle) { + + // Center of the triangle pillar - all polygons are given relative to this one + offsetResult.set( + (xi + 0.25) * elementSize, // sort of center of a triangle + (yi + 0.25) * elementSize, + h // vertical center + ); + + // Top triangle verts + verts[0].set( + -0.25 * elementSize, + -0.25 * elementSize, + data[xi][yi] - h + ); + verts[1].set( + 0.75 * elementSize, + -0.25 * elementSize, + data[xi + 1][yi] - h + ); + verts[2].set( + -0.25 * elementSize, + 0.75 * elementSize, + data[xi][yi + 1] - h + ); + + // bottom triangle verts + verts[3].set( + -0.25 * elementSize, + -0.25 * elementSize, + -h-1 + ); + verts[4].set( + 0.75 * elementSize, + -0.25 * elementSize, + -h-1 + ); + verts[5].set( + -0.25 * elementSize, + 0.75 * elementSize, + -h-1 + ); + + // top triangle + faces[0][0] = 0; + faces[0][1] = 1; + faces[0][2] = 2; + + // bottom triangle + faces[1][0] = 5; + faces[1][1] = 4; + faces[1][2] = 3; + + // -x facing quad + faces[2][0] = 0; + faces[2][1] = 2; + faces[2][2] = 5; + faces[2][3] = 3; + + // -y facing quad + faces[3][0] = 1; + faces[3][1] = 0; + faces[3][2] = 3; + faces[3][3] = 4; + + // +xy facing quad + faces[4][0] = 4; + faces[4][1] = 5; + faces[4][2] = 2; + faces[4][3] = 1; + + + } else { + + // Center of the triangle pillar - all polygons are given relative to this one + offsetResult.set( + (xi + 0.75) * elementSize, // sort of center of a triangle + (yi + 0.75) * elementSize, + h // vertical center + ); + + // Top triangle verts + verts[0].set( + 0.25 * elementSize, + 0.25 * elementSize, + data[xi + 1][yi + 1] - h + ); + verts[1].set( + -0.75 * elementSize, + 0.25 * elementSize, + data[xi][yi + 1] - h + ); + verts[2].set( + 0.25 * elementSize, + -0.75 * elementSize, + data[xi + 1][yi] - h + ); + + // bottom triangle verts + verts[3].set( + 0.25 * elementSize, + 0.25 * elementSize, + - h-1 + ); + verts[4].set( + -0.75 * elementSize, + 0.25 * elementSize, + - h-1 + ); + verts[5].set( + 0.25 * elementSize, + -0.75 * elementSize, + - h-1 + ); + + // Top triangle + faces[0][0] = 0; + faces[0][1] = 1; + faces[0][2] = 2; + + // bottom triangle + faces[1][0] = 5; + faces[1][1] = 4; + faces[1][2] = 3; + + // +x facing quad + faces[2][0] = 2; + faces[2][1] = 5; + faces[2][2] = 3; + faces[2][3] = 0; + + // +y facing quad + faces[3][0] = 3; + faces[3][1] = 4; + faces[3][2] = 1; + faces[3][3] = 0; + + // -xy facing quad + faces[4][0] = 1; + faces[4][1] = 4; + faces[4][2] = 5; + faces[4][3] = 2; + } + + result.computeNormals(); + result.computeEdges(); + result.updateBoundingSphereRadius(); + + this.setCachedConvexTrianglePillar(xi, yi, getUpperTriangle, result, offsetResult); +}; + +Heightfield.prototype.calculateLocalInertia = function(mass, target){ + target = target || new Vec3(); + target.set(0, 0, 0); + return target; +}; + +Heightfield.prototype.volume = function(){ + return Number.MAX_VALUE; // The terrain is infinite +}; + +Heightfield.prototype.calculateWorldAABB = function(pos, quat, min, max){ + // TODO: do it properly + min.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); + max.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); +}; + +Heightfield.prototype.updateBoundingSphereRadius = function(){ + // Use the bounding box of the min/max values + var data = this.data, + s = this.elementSize; + this.boundingSphereRadius = new Vec3(data.length * s, data[0].length * s, Math.max(Math.abs(this.maxValue), Math.abs(this.minValue))).norm(); +}; + +},{"../math/Vec3":30,"../utils/Utils":53,"./ConvexPolyhedron":38,"./Shape":43}],41:[function(_dereq_,module,exports){ +module.exports = Particle; + +var Shape = _dereq_('./Shape'); +var Vec3 = _dereq_('../math/Vec3'); + +/** + * Particle shape. + * @class Particle + * @constructor + * @author schteppe + * @extends Shape + */ +function Particle(){ + Shape.call(this); + + this.type = Shape.types.PARTICLE; +} +Particle.prototype = new Shape(); +Particle.prototype.constructor = Particle; + +/** + * @method calculateLocalInertia + * @param {Number} mass + * @param {Vec3} target + * @return {Vec3} + */ +Particle.prototype.calculateLocalInertia = function(mass,target){ + target = target || new Vec3(); + target.set(0, 0, 0); + return target; +}; + +Particle.prototype.volume = function(){ + return 0; +}; + +Particle.prototype.updateBoundingSphereRadius = function(){ + this.boundingSphereRadius = 0; +}; + +Particle.prototype.calculateWorldAABB = function(pos,quat,min,max){ + // Get each axis max + min.copy(pos); + max.copy(pos); +}; + +},{"../math/Vec3":30,"./Shape":43}],42:[function(_dereq_,module,exports){ +module.exports = Plane; + +var Shape = _dereq_('./Shape'); +var Vec3 = _dereq_('../math/Vec3'); + +/** + * A plane, facing in the Z direction. The plane has its surface at z=0 and everything below z=0 is assumed to be solid plane. To make the plane face in some other direction than z, you must put it inside a RigidBody and rotate that body. See the demos. + * @class Plane + * @constructor + * @extends Shape + * @author schteppe + */ +function Plane(){ + Shape.call(this); + this.type = Shape.types.PLANE; + + // World oriented normal + this.worldNormal = new Vec3(); + this.worldNormalNeedsUpdate = true; + + this.boundingSphereRadius = Number.MAX_VALUE; +} +Plane.prototype = new Shape(); +Plane.prototype.constructor = Plane; + +Plane.prototype.computeWorldNormal = function(quat){ + var n = this.worldNormal; + n.set(0,0,1); + quat.vmult(n,n); + this.worldNormalNeedsUpdate = false; +}; + +Plane.prototype.calculateLocalInertia = function(mass,target){ + target = target || new Vec3(); + return target; +}; + +Plane.prototype.volume = function(){ + return Number.MAX_VALUE; // The plane is infinite... +}; + +var tempNormal = new Vec3(); +Plane.prototype.calculateWorldAABB = function(pos, quat, min, max){ + // The plane AABB is infinite, except if the normal is pointing along any axis + tempNormal.set(0,0,1); // Default plane normal is z + quat.vmult(tempNormal,tempNormal); + var maxVal = Number.MAX_VALUE; + min.set(-maxVal, -maxVal, -maxVal); + max.set(maxVal, maxVal, maxVal); + + if(tempNormal.x === 1){ max.x = pos.x; } + if(tempNormal.y === 1){ max.y = pos.y; } + if(tempNormal.z === 1){ max.z = pos.z; } + + if(tempNormal.x === -1){ min.x = pos.x; } + if(tempNormal.y === -1){ min.y = pos.y; } + if(tempNormal.z === -1){ min.z = pos.z; } +}; + +Plane.prototype.updateBoundingSphereRadius = function(){ + this.boundingSphereRadius = Number.MAX_VALUE; +}; +},{"../math/Vec3":30,"./Shape":43}],43:[function(_dereq_,module,exports){ +module.exports = Shape; + +var Shape = _dereq_('./Shape'); +var Vec3 = _dereq_('../math/Vec3'); +var Quaternion = _dereq_('../math/Quaternion'); +var Material = _dereq_('../material/Material'); + +/** + * Base class for shapes + * @class Shape + * @constructor + * @author schteppe + * @todo Should have a mechanism for caching bounding sphere radius instead of calculating it each time + */ +function Shape(){ + + /** + * Identifyer of the Shape. + * @property {number} id + */ + this.id = Shape.idCounter++; + + /** + * The type of this shape. Must be set to an int > 0 by subclasses. + * @property type + * @type {Number} + * @see Shape.types + */ + this.type = 0; + + /** + * The local bounding sphere radius of this shape. + * @property {Number} boundingSphereRadius + */ + this.boundingSphereRadius = 0; + + /** + * Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled. + * @property {boolean} collisionResponse + */ + this.collisionResponse = true; + + /** + * @property {Material} material + */ + this.material = null; +} +Shape.prototype.constructor = Shape; + +/** + * Computes the bounding sphere radius. The result is stored in the property .boundingSphereRadius + * @method updateBoundingSphereRadius + * @return {Number} + */ +Shape.prototype.updateBoundingSphereRadius = function(){ + throw "computeBoundingSphereRadius() not implemented for shape type "+this.type; +}; + +/** + * Get the volume of this shape + * @method volume + * @return {Number} + */ +Shape.prototype.volume = function(){ + throw "volume() not implemented for shape type "+this.type; +}; + +/** + * Calculates the inertia in the local frame for this shape. + * @method calculateLocalInertia + * @return {Vec3} + * @see http://en.wikipedia.org/wiki/List_of_moments_of_inertia + */ +Shape.prototype.calculateLocalInertia = function(mass,target){ + throw "calculateLocalInertia() not implemented for shape type "+this.type; +}; + +Shape.idCounter = 0; + +/** + * The available shape types. + * @static + * @property types + * @type {Object} + */ +Shape.types = { + SPHERE:1, + PLANE:2, + BOX:4, + COMPOUND:8, + CONVEXPOLYHEDRON:16, + HEIGHTFIELD:32, + PARTICLE:64, + CYLINDER:128, + TRIMESH:256 +}; + + +},{"../material/Material":25,"../math/Quaternion":28,"../math/Vec3":30,"./Shape":43}],44:[function(_dereq_,module,exports){ +module.exports = Sphere; + +var Shape = _dereq_('./Shape'); +var Vec3 = _dereq_('../math/Vec3'); + +/** + * Spherical shape + * @class Sphere + * @constructor + * @extends Shape + * @param {Number} radius The radius of the sphere, a non-negative number. + * @author schteppe / http://github.com/schteppe + */ +function Sphere(radius){ + Shape.call(this); + + /** + * @property {Number} radius + */ + this.radius = radius!==undefined ? Number(radius) : 1.0; + this.type = Shape.types.SPHERE; + + if(this.radius < 0){ + throw new Error('The sphere radius cannot be negative.'); + } + + this.updateBoundingSphereRadius(); +} +Sphere.prototype = new Shape(); +Sphere.prototype.constructor = Sphere; + +Sphere.prototype.calculateLocalInertia = function(mass,target){ + target = target || new Vec3(); + var I = 2.0*mass*this.radius*this.radius/5.0; + target.x = I; + target.y = I; + target.z = I; + return target; +}; + +Sphere.prototype.volume = function(){ + return 4.0 * Math.PI * this.radius / 3.0; +}; + +Sphere.prototype.updateBoundingSphereRadius = function(){ + this.boundingSphereRadius = this.radius; +}; + +Sphere.prototype.calculateWorldAABB = function(pos,quat,min,max){ + var r = this.radius; + var axes = ['x','y','z']; + for(var i=0; i u.x){ + u.x = v.x; + } + + if(v.y < l.y){ + l.y = v.y; + } else if(v.y > u.y){ + u.y = v.y; + } + + if(v.z < l.z){ + l.z = v.z; + } else if(v.z > u.z){ + u.z = v.z; + } + } +}; + + +/** + * Update the .aabb property + * @method updateAABB + */ +Trimesh.prototype.updateAABB = function(){ + this.computeLocalAABB(this.aabb); +}; + +/** + * Will update the .boundingSphereRadius property + * @method updateBoundingSphereRadius + */ +Trimesh.prototype.updateBoundingSphereRadius = function(){ + // Assume points are distributed with local (0,0,0) as center + var max2 = 0; + var vertices = this.vertices; + var v = new Vec3(); + for(var i=0, N=vertices.length / 3; i !== N; i++) { + this.getVertex(i, v); + var norm2 = v.norm2(); + if(norm2 > max2){ + max2 = norm2; + } + } + this.boundingSphereRadius = Math.sqrt(max2); +}; + +var tempWorldVertex = new Vec3(); +var calculateWorldAABB_frame = new Transform(); +var calculateWorldAABB_aabb = new AABB(); + +/** + * @method calculateWorldAABB + * @param {Vec3} pos + * @param {Quaternion} quat + * @param {Vec3} min + * @param {Vec3} max + */ +Trimesh.prototype.calculateWorldAABB = function(pos,quat,min,max){ + /* + var n = this.vertices.length / 3, + verts = this.vertices; + var minx,miny,minz,maxx,maxy,maxz; + + var v = tempWorldVertex; + for(var i=0; i maxx || maxx===undefined){ + maxx = v.x; + } + + if (v.y < miny || miny===undefined){ + miny = v.y; + } else if(v.y > maxy || maxy===undefined){ + maxy = v.y; + } + + if (v.z < minz || minz===undefined){ + minz = v.z; + } else if(v.z > maxz || maxz===undefined){ + maxz = v.z; + } + } + min.set(minx,miny,minz); + max.set(maxx,maxy,maxz); + */ + + // Faster approximation using local AABB + var frame = calculateWorldAABB_frame; + var result = calculateWorldAABB_aabb; + frame.position = pos; + frame.quaternion = quat; + this.aabb.toWorldFrame(frame, result); + min.copy(result.lowerBound); + max.copy(result.upperBound); +}; + +/** + * Get approximate volume + * @method volume + * @return {Number} + */ +Trimesh.prototype.volume = function(){ + return 4.0 * Math.PI * this.boundingSphereRadius / 3.0; +}; + +/** + * Create a Trimesh instance, shaped as a torus. + * @static + * @method createTorus + * @param {number} [radius=1] + * @param {number} [tube=0.5] + * @param {number} [radialSegments=8] + * @param {number} [tubularSegments=6] + * @param {number} [arc=6.283185307179586] + * @return {Trimesh} A torus + */ +Trimesh.createTorus = function (radius, tube, radialSegments, tubularSegments, arc) { + radius = radius || 1; + tube = tube || 0.5; + radialSegments = radialSegments || 8; + tubularSegments = tubularSegments || 6; + arc = arc || Math.PI * 2; + + var vertices = []; + var indices = []; + + for ( var j = 0; j <= radialSegments; j ++ ) { + for ( var i = 0; i <= tubularSegments; i ++ ) { + var u = i / tubularSegments * arc; + var v = j / radialSegments * Math.PI * 2; + + var x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); + var y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); + var z = tube * Math.sin( v ); + + vertices.push( x, y, z ); + } + } + + for ( var j = 1; j <= radialSegments; j ++ ) { + for ( var i = 1; i <= tubularSegments; i ++ ) { + var a = ( tubularSegments + 1 ) * j + i - 1; + var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; + var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; + var d = ( tubularSegments + 1 ) * j + i; + + indices.push(a, b, d); + indices.push(b, c, d); + } + } + + return new Trimesh(vertices, indices); +}; + +},{"../collision/AABB":3,"../math/Quaternion":28,"../math/Transform":29,"../math/Vec3":30,"../utils/Octree":50,"./Shape":43}],46:[function(_dereq_,module,exports){ +module.exports = GSSolver; + +var Vec3 = _dereq_('../math/Vec3'); +var Quaternion = _dereq_('../math/Quaternion'); +var Solver = _dereq_('./Solver'); + +/** + * Constraint equation Gauss-Seidel solver. + * @class GSSolver + * @constructor + * @todo The spook parameters should be specified for each constraint, not globally. + * @author schteppe / https://github.com/schteppe + * @see https://www8.cs.umu.se/kurser/5DV058/VT09/lectures/spooknotes.pdf + * @extends Solver + */ +function GSSolver(){ + Solver.call(this); + + /** + * The number of solver iterations determines quality of the constraints in the world. The more iterations, the more correct simulation. More iterations need more computations though. If you have a large gravity force in your world, you will need more iterations. + * @property iterations + * @type {Number} + * @todo write more about solver and iterations in the wiki + */ + this.iterations = 10; + + /** + * When tolerance is reached, the system is assumed to be converged. + * @property tolerance + * @type {Number} + */ + this.tolerance = 1e-7; +} +GSSolver.prototype = new Solver(); + +var GSSolver_solve_lambda = []; // Just temporary number holders that we want to reuse each solve. +var GSSolver_solve_invCs = []; +var GSSolver_solve_Bs = []; +GSSolver.prototype.solve = function(dt,world){ + var iter = 0, + maxIter = this.iterations, + tolSquared = this.tolerance*this.tolerance, + equations = this.equations, + Neq = equations.length, + bodies = world.bodies, + Nbodies = bodies.length, + h = dt, + q, B, invC, deltalambda, deltalambdaTot, GWlambda, lambdaj; + + // Update solve mass + if(Neq !== 0){ + for(var i=0; i!==Nbodies; i++){ + bodies[i].updateSolveMassProperties(); + } + } + + // Things that does not change during iteration can be computed once + var invCs = GSSolver_solve_invCs, + Bs = GSSolver_solve_Bs, + lambda = GSSolver_solve_lambda; + invCs.length = Neq; + Bs.length = Neq; + lambda.length = Neq; + for(var i=0; i!==Neq; i++){ + var c = equations[i]; + lambda[i] = 0.0; + Bs[i] = c.computeB(h); + invCs[i] = 1.0 / c.computeC(); + } + + if(Neq !== 0){ + + // Reset vlambda + for(var i=0; i!==Nbodies; i++){ + var b=bodies[i], + vlambda=b.vlambda, + wlambda=b.wlambda; + vlambda.set(0,0,0); + if(wlambda){ + wlambda.set(0,0,0); + } + } + + // Iterate over equations + for(iter=0; iter!==maxIter; iter++){ + + // Accumulate the total error for each iteration. + deltalambdaTot = 0.0; + + for(var j=0; j!==Neq; j++){ + + var c = equations[j]; + + // Compute iteration + B = Bs[j]; + invC = invCs[j]; + lambdaj = lambda[j]; + GWlambda = c.computeGWlambda(); + deltalambda = invC * ( B - GWlambda - c.eps * lambdaj ); + + // Clamp if we are not within the min/max interval + if(lambdaj + deltalambda < c.minForce){ + deltalambda = c.minForce - lambdaj; + } else if(lambdaj + deltalambda > c.maxForce){ + deltalambda = c.maxForce - lambdaj; + } + lambda[j] += deltalambda; + + deltalambdaTot += deltalambda > 0.0 ? deltalambda : -deltalambda; // abs(deltalambda) + + c.addToWlambda(deltalambda); + } + + // If the total error is small enough - stop iterate + if(deltalambdaTot*deltalambdaTot < tolSquared){ + break; + } + } + + // Add result to velocity + for(var i=0; i!==Nbodies; i++){ + var b=bodies[i], + v=b.velocity, + w=b.angularVelocity; + v.vadd(b.vlambda, v); + if(w){ + w.vadd(b.wlambda, w); + } + } + } + + return iter; +}; + +},{"../math/Quaternion":28,"../math/Vec3":30,"./Solver":47}],47:[function(_dereq_,module,exports){ +module.exports = Solver; + +/** + * Constraint equation solver base class. + * @class Solver + * @constructor + * @author schteppe / https://github.com/schteppe + */ +function Solver(){ + /** + * All equations to be solved + * @property {Array} equations + */ + this.equations = []; +} + +/** + * Should be implemented in subclasses! + * @method solve + * @param {Number} dt + * @param {World} world + */ +Solver.prototype.solve = function(dt,world){ + // Should return the number of iterations done! + return 0; +}; + +/** + * Add an equation + * @method addEquation + * @param {Equation} eq + */ +Solver.prototype.addEquation = function(eq){ + if (eq.enabled) { + this.equations.push(eq); + } +}; + +/** + * Remove an equation + * @method removeEquation + * @param {Equation} eq + */ +Solver.prototype.removeEquation = function(eq){ + var eqs = this.equations; + var i = eqs.indexOf(eq); + if(i !== -1){ + eqs.splice(i,1); + } +}; + +/** + * Add all equations + * @method removeAllEquations + */ +Solver.prototype.removeAllEquations = function(){ + this.equations.length = 0; +}; + + +},{}],48:[function(_dereq_,module,exports){ +module.exports = SplitSolver; + +var Vec3 = _dereq_('../math/Vec3'); +var Quaternion = _dereq_('../math/Quaternion'); +var Solver = _dereq_('./Solver'); +var Body = _dereq_('../objects/Body'); + +/** + * Splits the equations into islands and solves them independently. Can improve performance. + * @class SplitSolver + * @constructor + * @extends Solver + * @param {Solver} subsolver + */ +function SplitSolver(subsolver){ + Solver.call(this); + this.iterations = 10; + this.tolerance = 1e-7; + this.subsolver = subsolver; + this.nodes = []; + this.nodePool = []; + + // Create needed nodes, reuse if possible + while(this.nodePool.length < 128){ + this.nodePool.push(this.createNode()); + } +} +SplitSolver.prototype = new Solver(); + +// Returns the number of subsystems +var SplitSolver_solve_nodes = []; // All allocated node objects +var SplitSolver_solve_nodePool = []; // All allocated node objects +var SplitSolver_solve_eqs = []; // Temp array +var SplitSolver_solve_bds = []; // Temp array +var SplitSolver_solve_dummyWorld = {bodies:[]}; // Temp object + +var STATIC = Body.STATIC; +function getUnvisitedNode(nodes){ + var Nnodes = nodes.length; + for(var i=0; i!==Nnodes; i++){ + var node = nodes[i]; + if(!node.visited && !(node.body.type & STATIC)){ + return node; + } + } + return false; +} + +var queue = []; +function bfs(root,visitFunc,bds,eqs){ + queue.push(root); + root.visited = true; + visitFunc(root,bds,eqs); + while(queue.length) { + var node = queue.pop(); + // Loop over unvisited child nodes + var child; + while((child = getUnvisitedNode(node.children))) { + child.visited = true; + visitFunc(child,bds,eqs); + queue.push(child); + } + } +} + +function visitFunc(node,bds,eqs){ + bds.push(node.body); + var Neqs = node.eqs.length; + for(var i=0; i!==Neqs; i++){ + var eq = node.eqs[i]; + if(eqs.indexOf(eq) === -1){ + eqs.push(eq); + } + } +} + +SplitSolver.prototype.createNode = function(){ + return { body:null, children:[], eqs:[], visited:false }; +}; + +/** + * Solve the subsystems + * @method solve + * @param {Number} dt + * @param {World} world + */ +SplitSolver.prototype.solve = function(dt,world){ + var nodes=SplitSolver_solve_nodes, + nodePool=this.nodePool, + bodies=world.bodies, + equations=this.equations, + Neq=equations.length, + Nbodies=bodies.length, + subsolver=this.subsolver; + + // Create needed nodes, reuse if possible + while(nodePool.length < Nbodies){ + nodePool.push(this.createNode()); + } + nodes.length = Nbodies; + for (var i = 0; i < Nbodies; i++) { + nodes[i] = nodePool[i]; + } + + // Reset node values + for(var i=0; i!==Nbodies; i++){ + var node = nodes[i]; + node.body = bodies[i]; + node.children.length = 0; + node.eqs.length = 0; + node.visited = false; + } + for(var k=0; k!==Neq; k++){ + var eq=equations[k], + i=bodies.indexOf(eq.bi), + j=bodies.indexOf(eq.bj), + ni=nodes[i], + nj=nodes[j]; + ni.children.push(nj); + ni.eqs.push(eq); + nj.children.push(ni); + nj.eqs.push(eq); + } + + var child, n=0, eqs=SplitSolver_solve_eqs; + + subsolver.tolerance = this.tolerance; + subsolver.iterations = this.iterations; + + var dummyWorld = SplitSolver_solve_dummyWorld; + while((child = getUnvisitedNode(nodes))){ + eqs.length = 0; + dummyWorld.bodies.length = 0; + bfs(child, visitFunc, dummyWorld.bodies, eqs); + + var Neqs = eqs.length; + + eqs = eqs.sort(sortById); + + for(var i=0; i!==Neqs; i++){ + subsolver.addEquation(eqs[i]); + } + + var iter = subsolver.solve(dt,dummyWorld); + subsolver.removeAllEquations(); + n++; + } + + return n; +}; + +function sortById(a, b){ + return b.id - a.id; +} +},{"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"./Solver":47}],49:[function(_dereq_,module,exports){ +/** + * Base class for objects that dispatches events. + * @class EventTarget + * @constructor + */ +var EventTarget = function () { + +}; + +module.exports = EventTarget; + +EventTarget.prototype = { + constructor: EventTarget, + + /** + * Add an event listener + * @method addEventListener + * @param {String} type + * @param {Function} listener + * @return {EventTarget} The self object, for chainability. + */ + 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 ); + } + return this; + }, + + /** + * Check if an event listener is added + * @method hasEventListener + * @param {String} type + * @param {Function} listener + * @return {Boolean} + */ + hasEventListener: function ( type, listener ) { + if ( this._listeners === undefined ){ return false; } + var listeners = this._listeners; + if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) { + return true; + } + return false; + }, + + /** + * Remove an event listener + * @method removeEventListener + * @param {String} type + * @param {Function} listener + * @return {EventTarget} The self object, for chainability. + */ + removeEventListener: function ( type, listener ) { + if ( this._listeners === undefined ){ return this; } + var listeners = this._listeners; + if ( listeners[type] === undefined ){ return this; } + var index = listeners[ type ].indexOf( listener ); + if ( index !== - 1 ) { + listeners[ type ].splice( index, 1 ); + } + return this; + }, + + /** + * Emit an event. + * @method dispatchEvent + * @param {Object} event + * @param {String} event.type + * @return {EventTarget} The self object, for chainability. + */ + dispatchEvent: function ( event ) { + if ( this._listeners === undefined ){ return this; } + var listeners = this._listeners; + var listenerArray = listeners[ event.type ]; + if ( listenerArray !== undefined ) { + event.target = this; + for ( var i = 0, l = listenerArray.length; i < l; i ++ ) { + listenerArray[ i ].call( this, event ); + } + } + return this; + } +}; + +},{}],50:[function(_dereq_,module,exports){ +var AABB = _dereq_('../collision/AABB'); +var Vec3 = _dereq_('../math/Vec3'); + +module.exports = Octree; + +/** + * @class OctreeNode + * @param {object} [options] + * @param {Octree} [options.root] + * @param {AABB} [options.aabb] + */ +function OctreeNode(options){ + options = options || {}; + + /** + * The root node + * @property {OctreeNode} root + */ + this.root = options.root || null; + + /** + * Boundary of this node + * @property {AABB} aabb + */ + this.aabb = options.aabb ? options.aabb.clone() : new AABB(); + + /** + * Contained data at the current node level. + * @property {Array} data + */ + this.data = []; + + /** + * Children to this node + * @property {Array} children + */ + this.children = []; +} + +/** + * @class Octree + * @param {AABB} aabb The total AABB of the tree + * @param {object} [options] + * @param {number} [options.maxDepth=8] + * @extends OctreeNode + */ +function Octree(aabb, options){ + options = options || {}; + options.root = null; + options.aabb = aabb; + OctreeNode.call(this, options); + + /** + * Maximum subdivision depth + * @property {number} maxDepth + */ + this.maxDepth = typeof(options.maxDepth) !== 'undefined' ? options.maxDepth : 8; +} +Octree.prototype = new OctreeNode(); + +OctreeNode.prototype.reset = function(aabb, options){ + this.children.length = this.data.length = 0; +}; + +/** + * Insert data into this node + * @method insert + * @param {AABB} aabb + * @param {object} elementData + * @return {boolean} True if successful, otherwise false + */ +OctreeNode.prototype.insert = function(aabb, elementData, level){ + var nodeData = this.data; + level = level || 0; + + // Ignore objects that do not belong in this node + if (!this.aabb.contains(aabb)){ + return false; // object cannot be added + } + + var children = this.children; + + if(level < (this.maxDepth || this.root.maxDepth)){ + // Subdivide if there are no children yet + var subdivided = false; + if (!children.length){ + this.subdivide(); + subdivided = true; + } + + // add to whichever node will accept it + for (var i = 0; i !== 8; i++) { + if (children[i].insert(aabb, elementData, level + 1)){ + return true; + } + } + + if(subdivided){ + // No children accepted! Might as well just remove em since they contain none + children.length = 0; + } + } + + // Too deep, or children didnt want it. add it in current node + nodeData.push(elementData); + + return true; +}; + +var halfDiagonal = new Vec3(); + +/** + * Create 8 equally sized children nodes and put them in the .children array. + * @method subdivide + */ +OctreeNode.prototype.subdivide = function() { + var aabb = this.aabb; + var l = aabb.lowerBound; + var u = aabb.upperBound; + + var children = this.children; + + children.push( + new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,0,0) }) }), + new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,0,0) }) }), + new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,1,0) }) }), + new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,1,1) }) }), + new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,1,1) }) }), + new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,0,1) }) }), + new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,0,1) }) }), + new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,1,0) }) }) + ); + + u.vsub(l, halfDiagonal); + halfDiagonal.scale(0.5, halfDiagonal); + + var root = this.root || this; + + for (var i = 0; i !== 8; i++) { + var child = children[i]; + + // Set current node as root + child.root = root; + + // Compute bounds + var lowerBound = child.aabb.lowerBound; + lowerBound.x *= halfDiagonal.x; + lowerBound.y *= halfDiagonal.y; + lowerBound.z *= halfDiagonal.z; + + lowerBound.vadd(l, lowerBound); + + // Upper bound is always lower bound + halfDiagonal + lowerBound.vadd(halfDiagonal, child.aabb.upperBound); + } +}; + +/** + * Get all data, potentially within an AABB + * @method aabbQuery + * @param {AABB} aabb + * @param {array} result + * @return {array} The "result" object + */ +OctreeNode.prototype.aabbQuery = function(aabb, result) { + + var nodeData = this.data; + + // abort if the range does not intersect this node + // if (!this.aabb.overlaps(aabb)){ + // return result; + // } + + // Add objects at this level + // Array.prototype.push.apply(result, nodeData); + + // Add child data + // @todo unwrap recursion into a queue / loop, that's faster in JS + var children = this.children; + + + // for (var i = 0, N = this.children.length; i !== N; i++) { + // children[i].aabbQuery(aabb, result); + // } + + var queue = [this]; + while (queue.length) { + var node = queue.pop(); + if (node.aabb.overlaps(aabb)){ + Array.prototype.push.apply(result, node.data); + } + Array.prototype.push.apply(queue, node.children); + } + + return result; +}; + +var tmpAABB = new AABB(); + +/** + * Get all data, potentially intersected by a ray. + * @method rayQuery + * @param {Ray} ray + * @param {Transform} treeTransform + * @param {array} result + * @return {array} The "result" object + */ +OctreeNode.prototype.rayQuery = function(ray, treeTransform, result) { + + // Use aabb query for now. + // @todo implement real ray query which needs less lookups + ray.getAABB(tmpAABB); + tmpAABB.toLocalFrame(treeTransform, tmpAABB); + this.aabbQuery(tmpAABB, result); + + return result; +}; + +/** + * @method removeEmptyNodes + */ +OctreeNode.prototype.removeEmptyNodes = function() { + var queue = [this]; + while (queue.length) { + var node = queue.pop(); + for (var i = node.children.length - 1; i >= 0; i--) { + if(!node.children[i].data.length){ + node.children.splice(i, 1); + } + } + Array.prototype.push.apply(queue, node.children); + } +}; + +},{"../collision/AABB":3,"../math/Vec3":30}],51:[function(_dereq_,module,exports){ +module.exports = Pool; + +/** + * For pooling objects that can be reused. + * @class Pool + * @constructor + */ +function Pool(){ + /** + * The pooled objects + * @property {Array} objects + */ + this.objects = []; + + /** + * Constructor of the objects + * @property {mixed} type + */ + this.type = Object; +} + +/** + * Release an object after use + * @method release + * @param {Object} obj + */ +Pool.prototype.release = function(){ + var Nargs = arguments.length; + for(var i=0; i!==Nargs; i++){ + this.objects.push(arguments[i]); + } +}; + +/** + * Get an object + * @method get + * @return {mixed} + */ +Pool.prototype.get = function(){ + if(this.objects.length===0){ + return this.constructObject(); + } else { + return this.objects.pop(); + } +}; + +/** + * Construct an object. Should be implmented in each subclass. + * @method constructObject + * @return {mixed} + */ +Pool.prototype.constructObject = function(){ + throw new Error("constructObject() not implemented in this Pool subclass yet!"); +}; + +},{}],52:[function(_dereq_,module,exports){ +module.exports = TupleDictionary; + +/** + * @class TupleDictionary + * @constructor + */ +function TupleDictionary() { + + /** + * The data storage + * @property data + * @type {Object} + */ + this.data = { keys:[] }; +} + +/** + * @method get + * @param {Number} i + * @param {Number} j + * @return {Number} + */ +TupleDictionary.prototype.get = function(i, j) { + if (i > j) { + // swap + var temp = j; + j = i; + i = temp; + } + return this.data[i+'-'+j]; +}; + +/** + * @method set + * @param {Number} i + * @param {Number} j + * @param {Number} value + */ +TupleDictionary.prototype.set = function(i, j, value) { + if (i > j) { + var temp = j; + j = i; + i = temp; + } + var key = i+'-'+j; + + // Check if key already exists + if(!this.get(i,j)){ + this.data.keys.push(key); + } + + this.data[key] = value; +}; + +/** + * @method reset + */ +TupleDictionary.prototype.reset = function() { + var data = this.data, + keys = data.keys; + while(keys.length > 0){ + var key = keys.pop(); + delete data[key]; + } +}; + +},{}],53:[function(_dereq_,module,exports){ +function Utils(){} + +module.exports = Utils; + +/** + * Extend an options object with default values. + * @static + * @method defaults + * @param {object} options The options object. May be falsy: in this case, a new object is created and returned. + * @param {object} defaults An object containing default values. + * @return {object} The modified options object. + */ +Utils.defaults = function(options, defaults){ + options = options || {}; + + for(var key in defaults){ + if(!(key in options)){ + options[key] = defaults[key]; + } + } + + return options; +}; + +},{}],54:[function(_dereq_,module,exports){ +module.exports = Vec3Pool; + +var Vec3 = _dereq_('../math/Vec3'); +var Pool = _dereq_('./Pool'); + +/** + * @class Vec3Pool + * @constructor + * @extends Pool + */ +function Vec3Pool(){ + Pool.call(this); + this.type = Vec3; +} +Vec3Pool.prototype = new Pool(); + +/** + * Construct a vector + * @method constructObject + * @return {Vec3} + */ +Vec3Pool.prototype.constructObject = function(){ + return new Vec3(); +}; + +},{"../math/Vec3":30,"./Pool":51}],55:[function(_dereq_,module,exports){ +module.exports = Narrowphase; + +var AABB = _dereq_('../collision/AABB'); +var Shape = _dereq_('../shapes/Shape'); +var Ray = _dereq_('../collision/Ray'); +var Vec3 = _dereq_('../math/Vec3'); +var Transform = _dereq_('../math/Transform'); +var ConvexPolyhedron = _dereq_('../shapes/ConvexPolyhedron'); +var Quaternion = _dereq_('../math/Quaternion'); +var Solver = _dereq_('../solver/Solver'); +var Vec3Pool = _dereq_('../utils/Vec3Pool'); +var ContactEquation = _dereq_('../equations/ContactEquation'); +var FrictionEquation = _dereq_('../equations/FrictionEquation'); + +/** + * Helper class for the World. Generates ContactEquations. + * @class Narrowphase + * @constructor + * @todo Sphere-ConvexPolyhedron contacts + * @todo Contact reduction + * @todo should move methods to prototype + */ +function Narrowphase(world){ + + /** + * Internal storage of pooled contact points. + * @property {Array} contactPointPool + */ + this.contactPointPool = []; + + this.frictionEquationPool = []; + + this.result = []; + this.frictionResult = []; + + /** + * Pooled vectors. + * @property {Vec3Pool} v3pool + */ + this.v3pool = new Vec3Pool(); + + this.world = world; + this.currentContactMaterial = null; + + /** + * @property {Boolean} enableFrictionReduction + */ + this.enableFrictionReduction = false; +} + +/** + * Make a contact object, by using the internal pool or creating a new one. + * @method createContactEquation + * @return {ContactEquation} + */ +Narrowphase.prototype.createContactEquation = function(bi, bj, si, sj, rsi, rsj){ + var c; + if(this.contactPointPool.length){ + c = this.contactPointPool.pop(); + c.bi = bi; + c.bj = bj; + } else { + c = new ContactEquation(bi, bj); + } + + c.enabled = bi.collisionResponse && bj.collisionResponse && si.collisionResponse && sj.collisionResponse; + + var cm = this.currentContactMaterial; + + c.restitution = cm.restitution; + + c.setSpookParams( + cm.contactEquationStiffness, + cm.contactEquationRelaxation, + this.world.dt + ); + + var matA = si.material || bi.material; + var matB = sj.material || bj.material; + if(matA && matB && matA.restitution >= 0 && matB.restitution >= 0){ + c.restitution = matA.restitution * matB.restitution; + } + + c.si = rsi || si; + c.sj = rsj || sj; + + return c; +}; + +Narrowphase.prototype.createFrictionEquationsFromContact = function(contactEquation, outArray){ + var bodyA = contactEquation.bi; + var bodyB = contactEquation.bj; + var shapeA = contactEquation.si; + var shapeB = contactEquation.sj; + + var world = this.world; + var cm = this.currentContactMaterial; + + // If friction or restitution were specified in the material, use them + var friction = cm.friction; + var matA = shapeA.material || bodyA.material; + var matB = shapeB.material || bodyB.material; + if(matA && matB && matA.friction >= 0 && matB.friction >= 0){ + friction = matA.friction * matB.friction; + } + + if(friction > 0){ + + // Create 2 tangent equations + var mug = friction * world.gravity.length(); + var reducedMass = (bodyA.invMass + bodyB.invMass); + if(reducedMass > 0){ + reducedMass = 1/reducedMass; + } + var pool = this.frictionEquationPool; + var c1 = pool.length ? pool.pop() : new FrictionEquation(bodyA,bodyB,mug*reducedMass); + var c2 = pool.length ? pool.pop() : new FrictionEquation(bodyA,bodyB,mug*reducedMass); + + c1.bi = c2.bi = bodyA; + c1.bj = c2.bj = bodyB; + c1.minForce = c2.minForce = -mug*reducedMass; + c1.maxForce = c2.maxForce = mug*reducedMass; + + // Copy over the relative vectors + c1.ri.copy(contactEquation.ri); + c1.rj.copy(contactEquation.rj); + c2.ri.copy(contactEquation.ri); + c2.rj.copy(contactEquation.rj); + + // Construct tangents + contactEquation.ni.tangents(c1.t, c2.t); + + // Set spook params + c1.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, world.dt); + c2.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, world.dt); + + c1.enabled = c2.enabled = contactEquation.enabled; + + outArray.push(c1, c2); + + return true; + } + + return false; +}; + +var averageNormal = new Vec3(); +var averageContactPointA = new Vec3(); +var averageContactPointB = new Vec3(); + +// Take the average N latest contact point on the plane. +Narrowphase.prototype.createFrictionFromAverage = function(numContacts){ + // The last contactEquation + var c = this.result[this.result.length - 1]; + + // Create the result: two "average" friction equations + if (!this.createFrictionEquationsFromContact(c, this.frictionResult) || numContacts === 1) { + return; + } + + var f1 = this.frictionResult[this.frictionResult.length - 2]; + var f2 = this.frictionResult[this.frictionResult.length - 1]; + + averageNormal.setZero(); + averageContactPointA.setZero(); + averageContactPointB.setZero(); + + var bodyA = c.bi; + var bodyB = c.bj; + for(var i=0; i!==numContacts; i++){ + c = this.result[this.result.length - 1 - i]; + if(c.bodyA !== bodyA){ + averageNormal.vadd(c.ni, averageNormal); // vec2.add(eq.t, eq.t, c.normalA); + averageContactPointA.vadd(c.ri, averageContactPointA); // vec2.add(eq.contactPointA, eq.contactPointA, c.contactPointA); + averageContactPointB.vadd(c.rj, averageContactPointB); + } else { + averageNormal.vsub(c.ni, averageNormal); // vec2.sub(eq.t, eq.t, c.normalA); + averageContactPointA.vadd(c.rj, averageContactPointA); // vec2.add(eq.contactPointA, eq.contactPointA, c.contactPointA); + averageContactPointB.vadd(c.ri, averageContactPointB); + } + } + + var invNumContacts = 1 / numContacts; + averageContactPointA.scale(invNumContacts, f1.ri); // vec2.scale(eq.contactPointA, eq.contactPointA, invNumContacts); + averageContactPointB.scale(invNumContacts, f1.rj); // vec2.scale(eq.contactPointB, eq.contactPointB, invNumContacts); + f2.ri.copy(f1.ri); // Should be the same + f2.rj.copy(f1.rj); + averageNormal.normalize(); + averageNormal.tangents(f1.t, f2.t); + // return eq; +}; + + +var tmpVec1 = new Vec3(); +var tmpVec2 = new Vec3(); +var tmpQuat1 = new Quaternion(); +var tmpQuat2 = new Quaternion(); + +/** + * Generate all contacts between a list of body pairs + * @method getContacts + * @param {array} p1 Array of body indices + * @param {array} p2 Array of body indices + * @param {World} world + * @param {array} result Array to store generated contacts + * @param {array} oldcontacts Optional. Array of reusable contact objects + */ +Narrowphase.prototype.getContacts = function(p1, p2, world, result, oldcontacts, frictionResult, frictionPool){ + // Save old contact objects + this.contactPointPool = oldcontacts; + this.frictionEquationPool = frictionPool; + this.result = result; + this.frictionResult = frictionResult; + + var qi = tmpQuat1; + var qj = tmpQuat2; + var xi = tmpVec1; + var xj = tmpVec2; + + for(var k=0, N=p1.length; k!==N; k++){ + + // Get current collision bodies + var bi = p1[k], + bj = p2[k]; + + // Get contact material + var bodyContactMaterial = null; + if(bi.material && bj.material){ + bodyContactMaterial = world.getContactMaterial(bi.material,bj.material) || null; + } + + for (var i = 0; i < bi.shapes.length; i++) { + bi.quaternion.mult(bi.shapeOrientations[i], qi); + bi.quaternion.vmult(bi.shapeOffsets[i], xi); + xi.vadd(bi.position, xi); + var si = bi.shapes[i]; + + for (var j = 0; j < bj.shapes.length; j++) { + + // Compute world transform of shapes + bj.quaternion.mult(bj.shapeOrientations[j], qj); + bj.quaternion.vmult(bj.shapeOffsets[j], xj); + xj.vadd(bj.position, xj); + var sj = bj.shapes[j]; + + if(xi.distanceTo(xj) > si.boundingSphereRadius + sj.boundingSphereRadius){ + continue; + } + + // Get collision material + var shapeContactMaterial = null; + if(si.material && sj.material){ + shapeContactMaterial = world.getContactMaterial(si.material,sj.material) || null; + } + + this.currentContactMaterial = shapeContactMaterial || bodyContactMaterial || world.defaultContactMaterial; + + // Get contacts + var resolver = this[si.type | sj.type]; + if(resolver){ + if (si.type < sj.type) { + resolver.call(this, si, sj, xi, xj, qi, qj, bi, bj, si, sj); + } else { + resolver.call(this, sj, si, xj, xi, qj, qi, bj, bi, si, sj); + } + } + } + } + } +}; + +var numWarnings = 0; +var maxWarnings = 10; + +function warn(msg){ + if(numWarnings > maxWarnings){ + return; + } + + numWarnings++; + + console.warn(msg); +} + +Narrowphase.prototype[Shape.types.BOX | Shape.types.BOX] = +Narrowphase.prototype.boxBox = function(si,sj,xi,xj,qi,qj,bi,bj){ + si.convexPolyhedronRepresentation.material = si.material; + sj.convexPolyhedronRepresentation.material = sj.material; + si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; + sj.convexPolyhedronRepresentation.collisionResponse = sj.collisionResponse; + this.convexConvex(si.convexPolyhedronRepresentation,sj.convexPolyhedronRepresentation,xi,xj,qi,qj,bi,bj,si,sj); +}; + +Narrowphase.prototype[Shape.types.BOX | Shape.types.CONVEXPOLYHEDRON] = +Narrowphase.prototype.boxConvex = function(si,sj,xi,xj,qi,qj,bi,bj){ + si.convexPolyhedronRepresentation.material = si.material; + si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; + this.convexConvex(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj,si,sj); +}; + +Narrowphase.prototype[Shape.types.BOX | Shape.types.PARTICLE] = +Narrowphase.prototype.boxParticle = function(si,sj,xi,xj,qi,qj,bi,bj){ + si.convexPolyhedronRepresentation.material = si.material; + si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; + this.convexParticle(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj,si,sj); +}; + +/** + * @method sphereSphere + * @param {Shape} si + * @param {Shape} sj + * @param {Vec3} xi + * @param {Vec3} xj + * @param {Quaternion} qi + * @param {Quaternion} qj + * @param {Body} bi + * @param {Body} bj + */ +Narrowphase.prototype[Shape.types.SPHERE] = +Narrowphase.prototype.sphereSphere = function(si,sj,xi,xj,qi,qj,bi,bj){ + // We will have only one contact in this case + var r = this.createContactEquation(bi,bj,si,sj); + + // Contact normal + xj.vsub(xi, r.ni); + r.ni.normalize(); + + // Contact point locations + r.ri.copy(r.ni); + r.rj.copy(r.ni); + r.ri.mult(si.radius, r.ri); + r.rj.mult(-sj.radius, r.rj); + + r.ri.vadd(xi, r.ri); + r.ri.vsub(bi.position, r.ri); + + r.rj.vadd(xj, r.rj); + r.rj.vsub(bj.position, r.rj); + + this.result.push(r); + + this.createFrictionEquationsFromContact(r, this.frictionResult); +}; + +/** + * @method planeTrimesh + * @param {Shape} si + * @param {Shape} sj + * @param {Vec3} xi + * @param {Vec3} xj + * @param {Quaternion} qi + * @param {Quaternion} qj + * @param {Body} bi + * @param {Body} bj + */ +var planeTrimesh_normal = new Vec3(); +var planeTrimesh_relpos = new Vec3(); +var planeTrimesh_projected = new Vec3(); +Narrowphase.prototype[Shape.types.PLANE | Shape.types.TRIMESH] = +Narrowphase.prototype.planeTrimesh = function( + planeShape, + trimeshShape, + planePos, + trimeshPos, + planeQuat, + trimeshQuat, + planeBody, + trimeshBody +){ + // Make contacts! + var v = new Vec3(); + + var normal = planeTrimesh_normal; + normal.set(0,0,1); + planeQuat.vmult(normal,normal); // Turn normal according to plane + + for(var i=0; i 0 && positionAlongEdgeB < 0){ + + // Now check the orthogonal distance from edge to sphere center + localSpherePos.vsub(edgeVertexA, tmp); + + edgeVectorUnit.copy(edgeVector); + edgeVectorUnit.normalize(); + positionAlongEdgeA = tmp.dot(edgeVectorUnit); + + edgeVectorUnit.scale(positionAlongEdgeA, tmp); + tmp.vadd(edgeVertexA, tmp); + + // tmp is now the sphere center position projected to the edge, defined locally in the trimesh frame + var dist = tmp.distanceTo(localSpherePos); + if(dist < sphereShape.radius){ + var r = this.createContactEquation(sphereBody, trimeshBody, sphereShape, trimeshShape); + + tmp.vsub(localSpherePos, r.ni); + r.ni.normalize(); + r.ni.scale(sphereShape.radius, r.ri); + + Transform.pointToWorldFrame(trimeshPos, trimeshQuat, tmp, tmp); + tmp.vsub(trimeshBody.position, r.rj); + + Transform.vectorToWorldFrame(trimeshQuat, r.ni, r.ni); + Transform.vectorToWorldFrame(trimeshQuat, r.ri, r.ri); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + } + } + + // Triangle faces + var va = sphereTrimesh_va; + var vb = sphereTrimesh_vb; + var vc = sphereTrimesh_vc; + var normal = sphereTrimesh_normal; + for(var i=0, N = triangles.length; i !== N; i++){ + trimeshShape.getTriangleVertices(triangles[i], va, vb, vc); + trimeshShape.getNormal(triangles[i], normal); + localSpherePos.vsub(va, tmp); + var dist = tmp.dot(normal); + normal.scale(dist, tmp); + localSpherePos.vsub(tmp, tmp); + + // tmp is now the sphere position projected to the triangle plane + dist = tmp.distanceTo(localSpherePos); + if(Ray.pointInTriangle(tmp, va, vb, vc) && dist < sphereShape.radius){ + var r = this.createContactEquation(sphereBody, trimeshBody, sphereShape, trimeshShape); + + tmp.vsub(localSpherePos, r.ni); + r.ni.normalize(); + r.ni.scale(sphereShape.radius, r.ri); + + Transform.pointToWorldFrame(trimeshPos, trimeshQuat, tmp, tmp); + tmp.vsub(trimeshBody.position, r.rj); + + Transform.vectorToWorldFrame(trimeshQuat, r.ni, r.ni); + Transform.vectorToWorldFrame(trimeshQuat, r.ri, r.ri); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + + triangles.length = 0; +}; + +var point_on_plane_to_sphere = new Vec3(); +var plane_to_sphere_ortho = new Vec3(); + +/** + * @method spherePlane + * @param {Shape} si + * @param {Shape} sj + * @param {Vec3} xi + * @param {Vec3} xj + * @param {Quaternion} qi + * @param {Quaternion} qj + * @param {Body} bi + * @param {Body} bj + */ +Narrowphase.prototype[Shape.types.SPHERE | Shape.types.PLANE] = +Narrowphase.prototype.spherePlane = function(si,sj,xi,xj,qi,qj,bi,bj){ + // We will have one contact in this case + var r = this.createContactEquation(bi,bj,si,sj); + + // Contact normal + r.ni.set(0,0,1); + qj.vmult(r.ni, r.ni); + r.ni.negate(r.ni); // body i is the sphere, flip normal + r.ni.normalize(); // Needed? + + // Vector from sphere center to contact point + r.ni.mult(si.radius, r.ri); + + // Project down sphere on plane + xi.vsub(xj, point_on_plane_to_sphere); + r.ni.mult(r.ni.dot(point_on_plane_to_sphere), plane_to_sphere_ortho); + point_on_plane_to_sphere.vsub(plane_to_sphere_ortho,r.rj); // The sphere position projected to plane + + if(-point_on_plane_to_sphere.dot(r.ni) <= si.radius){ + + // Make it relative to the body + var ri = r.ri; + var rj = r.rj; + ri.vadd(xi, ri); + ri.vsub(bi.position, ri); + rj.vadd(xj, rj); + rj.vsub(bj.position, rj); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } +}; + +// See http://bulletphysics.com/Bullet/BulletFull/SphereTriangleDetector_8cpp_source.html +var pointInPolygon_edge = new Vec3(); +var pointInPolygon_edge_x_normal = new Vec3(); +var pointInPolygon_vtp = new Vec3(); +function pointInPolygon(verts, normal, p){ + var positiveResult = null; + var N = verts.length; + for(var i=0; i!==N; i++){ + var v = verts[i]; + + // Get edge to the next vertex + var edge = pointInPolygon_edge; + verts[(i+1) % (N)].vsub(v,edge); + + // Get cross product between polygon normal and the edge + var edge_x_normal = pointInPolygon_edge_x_normal; + //var edge_x_normal = new Vec3(); + edge.cross(normal,edge_x_normal); + + // Get vector between point and current vertex + var vertex_to_p = pointInPolygon_vtp; + p.vsub(v,vertex_to_p); + + // This dot product determines which side of the edge the point is + var r = edge_x_normal.dot(vertex_to_p); + + // If all such dot products have same sign, we are inside the polygon. + if(positiveResult===null || (r>0 && positiveResult===true) || (r<=0 && positiveResult===false)){ + if(positiveResult===null){ + positiveResult = r>0; + } + continue; + } else { + return false; // Encountered some other sign. Exit. + } + } + + // If we got here, all dot products were of the same sign. + return true; +} + +var box_to_sphere = new Vec3(); +var sphereBox_ns = new Vec3(); +var sphereBox_ns1 = new Vec3(); +var sphereBox_ns2 = new Vec3(); +var sphereBox_sides = [new Vec3(),new Vec3(),new Vec3(),new Vec3(),new Vec3(),new Vec3()]; +var sphereBox_sphere_to_corner = new Vec3(); +var sphereBox_side_ns = new Vec3(); +var sphereBox_side_ns1 = new Vec3(); +var sphereBox_side_ns2 = new Vec3(); + +/** + * @method sphereBox + * @param {Shape} si + * @param {Shape} sj + * @param {Vec3} xi + * @param {Vec3} xj + * @param {Quaternion} qi + * @param {Quaternion} qj + * @param {Body} bi + * @param {Body} bj + */ +Narrowphase.prototype[Shape.types.SPHERE | Shape.types.BOX] = +Narrowphase.prototype.sphereBox = function(si,sj,xi,xj,qi,qj,bi,bj){ + var v3pool = this.v3pool; + + // we refer to the box as body j + var sides = sphereBox_sides; + xi.vsub(xj,box_to_sphere); + sj.getSideNormals(sides,qj); + var R = si.radius; + var penetrating_sides = []; + + // Check side (plane) intersections + var found = false; + + // Store the resulting side penetration info + var side_ns = sphereBox_side_ns; + var side_ns1 = sphereBox_side_ns1; + var side_ns2 = sphereBox_side_ns2; + var side_h = null; + var side_penetrations = 0; + var side_dot1 = 0; + var side_dot2 = 0; + var side_distance = null; + for(var idx=0,nsides=sides.length; idx!==nsides && found===false; idx++){ + // Get the plane side normal (ns) + var ns = sphereBox_ns; + ns.copy(sides[idx]); + + var h = ns.norm(); + ns.normalize(); + + // The normal/distance dot product tells which side of the plane we are + var dot = box_to_sphere.dot(ns); + + if(dot0){ + // Intersects plane. Now check the other two dimensions + var ns1 = sphereBox_ns1; + var ns2 = sphereBox_ns2; + ns1.copy(sides[(idx+1)%3]); + ns2.copy(sides[(idx+2)%3]); + var h1 = ns1.norm(); + var h2 = ns2.norm(); + ns1.normalize(); + ns2.normalize(); + var dot1 = box_to_sphere.dot(ns1); + var dot2 = box_to_sphere.dot(ns2); + if(dot1

-h1 && dot2

-h2){ + var dist = Math.abs(dot-h-R); + if(side_distance===null || dist < side_distance){ + side_distance = dist; + side_dot1 = dot1; + side_dot2 = dot2; + side_h = h; + side_ns.copy(ns); + side_ns1.copy(ns1); + side_ns2.copy(ns2); + side_penetrations++; + } + } + } + } + if(side_penetrations){ + found = true; + var r = this.createContactEquation(bi,bj,si,sj); + side_ns.mult(-R,r.ri); // Sphere r + r.ni.copy(side_ns); + r.ni.negate(r.ni); // Normal should be out of sphere + side_ns.mult(side_h,side_ns); + side_ns1.mult(side_dot1,side_ns1); + side_ns.vadd(side_ns1,side_ns); + side_ns2.mult(side_dot2,side_ns2); + side_ns.vadd(side_ns2,r.rj); + + // Make relative to bodies + r.ri.vadd(xi, r.ri); + r.ri.vsub(bi.position, r.ri); + r.rj.vadd(xj, r.rj); + r.rj.vsub(bj.position, r.rj); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + + // Check corners + var rj = v3pool.get(); + var sphere_to_corner = sphereBox_sphere_to_corner; + for(var j=0; j!==2 && !found; j++){ + for(var k=0; k!==2 && !found; k++){ + for(var l=0; l!==2 && !found; l++){ + rj.set(0,0,0); + if(j){ + rj.vadd(sides[0],rj); + } else { + rj.vsub(sides[0],rj); + } + if(k){ + rj.vadd(sides[1],rj); + } else { + rj.vsub(sides[1],rj); + } + if(l){ + rj.vadd(sides[2],rj); + } else { + rj.vsub(sides[2],rj); + } + + // World position of corner + xj.vadd(rj,sphere_to_corner); + sphere_to_corner.vsub(xi,sphere_to_corner); + + if(sphere_to_corner.norm2() < R*R){ + found = true; + var r = this.createContactEquation(bi,bj,si,sj); + r.ri.copy(sphere_to_corner); + r.ri.normalize(); + r.ni.copy(r.ri); + r.ri.mult(R,r.ri); + r.rj.copy(rj); + + // Make relative to bodies + r.ri.vadd(xi, r.ri); + r.ri.vsub(bi.position, r.ri); + r.rj.vadd(xj, r.rj); + r.rj.vsub(bj.position, r.rj); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + } + } + v3pool.release(rj); + rj = null; + + // Check edges + var edgeTangent = v3pool.get(); + var edgeCenter = v3pool.get(); + var r = v3pool.get(); // r = edge center to sphere center + var orthogonal = v3pool.get(); + var dist = v3pool.get(); + var Nsides = sides.length; + for(var j=0; j!==Nsides && !found; j++){ + for(var k=0; k!==Nsides && !found; k++){ + if(j%3 !== k%3){ + // Get edge tangent + sides[k].cross(sides[j],edgeTangent); + edgeTangent.normalize(); + sides[j].vadd(sides[k], edgeCenter); + r.copy(xi); + r.vsub(edgeCenter,r); + r.vsub(xj,r); + var orthonorm = r.dot(edgeTangent); // distance from edge center to sphere center in the tangent direction + edgeTangent.mult(orthonorm,orthogonal); // Vector from edge center to sphere center in the tangent direction + + // Find the third side orthogonal to this one + var l = 0; + while(l===j%3 || l===k%3){ + l++; + } + + // vec from edge center to sphere projected to the plane orthogonal to the edge tangent + dist.copy(xi); + dist.vsub(orthogonal,dist); + dist.vsub(edgeCenter,dist); + dist.vsub(xj,dist); + + // Distances in tangent direction and distance in the plane orthogonal to it + var tdist = Math.abs(orthonorm); + var ndist = dist.norm(); + + if(tdist < sides[l].norm() && ndist si.boundingSphereRadius + sj.boundingSphereRadius){ + // return; + // } + + // Check corners + for(var i=0; i!==verts.length; i++){ + var v = verts[i]; + + // World position of corner + var worldCorner = sphereConvex_worldCorner; + qj.vmult(v,worldCorner); + xj.vadd(worldCorner,worldCorner); + var sphere_to_corner = sphereConvex_sphereToCorner; + worldCorner.vsub(xi, sphere_to_corner); + if(sphere_to_corner.norm2() < R * R){ + found = true; + var r = this.createContactEquation(bi,bj,si,sj); + r.ri.copy(sphere_to_corner); + r.ri.normalize(); + r.ni.copy(r.ri); + r.ri.mult(R,r.ri); + worldCorner.vsub(xj,r.rj); + + // Should be relative to the body. + r.ri.vadd(xi, r.ri); + r.ri.vsub(bi.position, r.ri); + + // Should be relative to the body. + r.rj.vadd(xj, r.rj); + r.rj.vsub(bj.position, r.rj); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + return; + } + } + + // Check side (plane) intersections + var found = false; + for(var i=0, nfaces=faces.length; i!==nfaces && found===false; i++){ + var normal = normals[i]; + var face = faces[i]; + + // Get world-transformed normal of the face + var worldNormal = sphereConvex_worldNormal; + qj.vmult(normal,worldNormal); + + // Get a world vertex from the face + var worldPoint = sphereConvex_worldPoint; + qj.vmult(verts[face[0]],worldPoint); + worldPoint.vadd(xj,worldPoint); + + // Get a point on the sphere, closest to the face normal + var worldSpherePointClosestToPlane = sphereConvex_worldSpherePointClosestToPlane; + worldNormal.mult(-R, worldSpherePointClosestToPlane); + xi.vadd(worldSpherePointClosestToPlane, worldSpherePointClosestToPlane); + + // Vector from a face point to the closest point on the sphere + var penetrationVec = sphereConvex_penetrationVec; + worldSpherePointClosestToPlane.vsub(worldPoint,penetrationVec); + + // The penetration. Negative value means overlap. + var penetration = penetrationVec.dot(worldNormal); + + var worldPointToSphere = sphereConvex_sphereToWorldPoint; + xi.vsub(worldPoint, worldPointToSphere); + + if(penetration < 0 && worldPointToSphere.dot(worldNormal)>0){ + // Intersects plane. Now check if the sphere is inside the face polygon + var faceVerts = []; // Face vertices, in world coords + for(var j=0, Nverts=face.length; j!==Nverts; j++){ + var worldVertex = v3pool.get(); + qj.vmult(verts[face[j]], worldVertex); + xj.vadd(worldVertex,worldVertex); + faceVerts.push(worldVertex); + } + + if(pointInPolygon(faceVerts,worldNormal,xi)){ // Is the sphere center in the face polygon? + found = true; + var r = this.createContactEquation(bi,bj,si,sj); + + worldNormal.mult(-R, r.ri); // Contact offset, from sphere center to contact + worldNormal.negate(r.ni); // Normal pointing out of sphere + + var penetrationVec2 = v3pool.get(); + worldNormal.mult(-penetration, penetrationVec2); + var penetrationSpherePoint = v3pool.get(); + worldNormal.mult(-R, penetrationSpherePoint); + + //xi.vsub(xj).vadd(penetrationSpherePoint).vadd(penetrationVec2 , r.rj); + xi.vsub(xj,r.rj); + r.rj.vadd(penetrationSpherePoint,r.rj); + r.rj.vadd(penetrationVec2 , r.rj); + + // Should be relative to the body. + r.rj.vadd(xj, r.rj); + r.rj.vsub(bj.position, r.rj); + + // Should be relative to the body. + r.ri.vadd(xi, r.ri); + r.ri.vsub(bi.position, r.ri); + + v3pool.release(penetrationVec2); + v3pool.release(penetrationSpherePoint); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + + // Release world vertices + for(var j=0, Nfaceverts=faceVerts.length; j!==Nfaceverts; j++){ + v3pool.release(faceVerts[j]); + } + + return; // We only expect *one* face contact + } else { + // Edge? + for(var j=0; j!==face.length; j++){ + + // Get two world transformed vertices + var v1 = v3pool.get(); + var v2 = v3pool.get(); + qj.vmult(verts[face[(j+1)%face.length]], v1); + qj.vmult(verts[face[(j+2)%face.length]], v2); + xj.vadd(v1, v1); + xj.vadd(v2, v2); + + // Construct edge vector + var edge = sphereConvex_edge; + v2.vsub(v1,edge); + + // Construct the same vector, but normalized + var edgeUnit = sphereConvex_edgeUnit; + edge.unit(edgeUnit); + + // p is xi projected onto the edge + var p = v3pool.get(); + var v1_to_xi = v3pool.get(); + xi.vsub(v1, v1_to_xi); + var dot = v1_to_xi.dot(edgeUnit); + edgeUnit.mult(dot, p); + p.vadd(v1, p); + + // Compute a vector from p to the center of the sphere + var xi_to_p = v3pool.get(); + p.vsub(xi, xi_to_p); + + // Collision if the edge-sphere distance is less than the radius + // AND if p is in between v1 and v2 + if(dot > 0 && dot*dot si.boundingSphereRadius + sj.boundingSphereRadius){ + return; + } + + if(si.findSeparatingAxis(sj,xi,qi,xj,qj,sepAxis,faceListA,faceListB)){ + var res = []; + var q = convexConvex_q; + si.clipAgainstHull(xi,qi,sj,xj,qj,sepAxis,-100,100,res); + var numContacts = 0; + for(var j = 0; j !== res.length; j++){ + var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj), + ri = r.ri, + rj = r.rj; + sepAxis.negate(r.ni); + res[j].normal.negate(q); + q.mult(res[j].depth, q); + res[j].point.vadd(q, ri); + rj.copy(res[j].point); + + // Contact points are in world coordinates. Transform back to relative + ri.vsub(xi,ri); + rj.vsub(xj,rj); + + // Make relative to bodies + ri.vadd(xi, ri); + ri.vsub(bi.position, ri); + rj.vadd(xj, rj); + rj.vsub(bj.position, rj); + + this.result.push(r); + numContacts++; + if(!this.enableFrictionReduction){ + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + if(this.enableFrictionReduction && numContacts){ + this.createFrictionFromAverage(numContacts); + } + } +}; + + +/** + * @method convexTrimesh + * @param {Array} result + * @param {Shape} si + * @param {Shape} sj + * @param {Vec3} xi + * @param {Vec3} xj + * @param {Quaternion} qi + * @param {Quaternion} qj + * @param {Body} bi + * @param {Body} bj + */ +// Narrowphase.prototype[Shape.types.CONVEXPOLYHEDRON | Shape.types.TRIMESH] = +// Narrowphase.prototype.convexTrimesh = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,faceListA,faceListB){ +// var sepAxis = convexConvex_sepAxis; + +// if(xi.distanceTo(xj) > si.boundingSphereRadius + sj.boundingSphereRadius){ +// return; +// } + +// // Construct a temp hull for each triangle +// var hullB = new ConvexPolyhedron(); + +// hullB.faces = [[0,1,2]]; +// var va = new Vec3(); +// var vb = new Vec3(); +// var vc = new Vec3(); +// hullB.vertices = [ +// va, +// vb, +// vc +// ]; + +// for (var i = 0; i < sj.indices.length / 3; i++) { + +// var triangleNormal = new Vec3(); +// sj.getNormal(i, triangleNormal); +// hullB.faceNormals = [triangleNormal]; + +// sj.getTriangleVertices(i, va, vb, vc); + +// var d = si.testSepAxis(triangleNormal, hullB, xi, qi, xj, qj); +// if(!d){ +// triangleNormal.scale(-1, triangleNormal); +// d = si.testSepAxis(triangleNormal, hullB, xi, qi, xj, qj); + +// if(!d){ +// continue; +// } +// } + +// var res = []; +// var q = convexConvex_q; +// si.clipAgainstHull(xi,qi,hullB,xj,qj,triangleNormal,-100,100,res); +// for(var j = 0; j !== res.length; j++){ +// var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj), +// ri = r.ri, +// rj = r.rj; +// r.ni.copy(triangleNormal); +// r.ni.negate(r.ni); +// res[j].normal.negate(q); +// q.mult(res[j].depth, q); +// res[j].point.vadd(q, ri); +// rj.copy(res[j].point); + +// // Contact points are in world coordinates. Transform back to relative +// ri.vsub(xi,ri); +// rj.vsub(xj,rj); + +// // Make relative to bodies +// ri.vadd(xi, ri); +// ri.vsub(bi.position, ri); +// rj.vadd(xj, rj); +// rj.vsub(bj.position, rj); + +// result.push(r); +// } +// } +// }; + +var particlePlane_normal = new Vec3(); +var particlePlane_relpos = new Vec3(); +var particlePlane_projected = new Vec3(); + +/** + * @method particlePlane + * @param {Array} result + * @param {Shape} si + * @param {Shape} sj + * @param {Vec3} xi + * @param {Vec3} xj + * @param {Quaternion} qi + * @param {Quaternion} qj + * @param {Body} bi + * @param {Body} bj + */ +Narrowphase.prototype[Shape.types.PLANE | Shape.types.PARTICLE] = +Narrowphase.prototype.planeParticle = function(sj,si,xj,xi,qj,qi,bj,bi){ + var normal = particlePlane_normal; + normal.set(0,0,1); + bj.quaternion.vmult(normal,normal); // Turn normal according to plane orientation + var relpos = particlePlane_relpos; + xi.vsub(bj.position,relpos); + var dot = normal.dot(relpos); + if(dot <= 0.0){ + var r = this.createContactEquation(bi,bj,si,sj); + r.ni.copy(normal); // Contact normal is the plane normal + r.ni.negate(r.ni); + r.ri.set(0,0,0); // Center of particle + + // Get particle position projected on plane + var projected = particlePlane_projected; + normal.mult(normal.dot(xi),projected); + xi.vsub(projected,projected); + //projected.vadd(bj.position,projected); + + // rj is now the projected world position minus plane position + r.rj.copy(projected); + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } +}; + +var particleSphere_normal = new Vec3(); + +/** + * @method particleSphere + * @param {Array} result + * @param {Shape} si + * @param {Shape} sj + * @param {Vec3} xi + * @param {Vec3} xj + * @param {Quaternion} qi + * @param {Quaternion} qj + * @param {Body} bi + * @param {Body} bj + */ +Narrowphase.prototype[Shape.types.PARTICLE | Shape.types.SPHERE] = +Narrowphase.prototype.sphereParticle = function(sj,si,xj,xi,qj,qi,bj,bi){ + // The normal is the unit vector from sphere center to particle center + var normal = particleSphere_normal; + normal.set(0,0,1); + xi.vsub(xj,normal); + var lengthSquared = normal.norm2(); + + if(lengthSquared <= sj.radius * sj.radius){ + var r = this.createContactEquation(bi,bj,si,sj); + normal.normalize(); + r.rj.copy(normal); + r.rj.mult(sj.radius,r.rj); + r.ni.copy(normal); // Contact normal + r.ni.negate(r.ni); + r.ri.set(0,0,0); // Center of particle + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } +}; + +// WIP +var cqj = new Quaternion(); +var convexParticle_local = new Vec3(); +var convexParticle_normal = new Vec3(); +var convexParticle_penetratedFaceNormal = new Vec3(); +var convexParticle_vertexToParticle = new Vec3(); +var convexParticle_worldPenetrationVec = new Vec3(); + +/** + * @method convexParticle + * @param {Array} result + * @param {Shape} si + * @param {Shape} sj + * @param {Vec3} xi + * @param {Vec3} xj + * @param {Quaternion} qi + * @param {Quaternion} qj + * @param {Body} bi + * @param {Body} bj + */ +Narrowphase.prototype[Shape.types.PARTICLE | Shape.types.CONVEXPOLYHEDRON] = +Narrowphase.prototype.convexParticle = function(sj,si,xj,xi,qj,qi,bj,bi){ + var penetratedFaceIndex = -1; + var penetratedFaceNormal = convexParticle_penetratedFaceNormal; + var worldPenetrationVec = convexParticle_worldPenetrationVec; + var minPenetration = null; + var numDetectedFaces = 0; + + // Convert particle position xi to local coords in the convex + var local = convexParticle_local; + local.copy(xi); + local.vsub(xj,local); // Convert position to relative the convex origin + qj.conjugate(cqj); + cqj.vmult(local,local); + + if(sj.pointIsInside(local)){ + + if(sj.worldVerticesNeedsUpdate){ + sj.computeWorldVertices(xj,qj); + } + if(sj.worldFaceNormalsNeedsUpdate){ + sj.computeWorldFaceNormals(qj); + } + + // For each world polygon in the polyhedra + for(var i=0,nfaces=sj.faces.length; i!==nfaces; i++){ + + // Construct world face vertices + var verts = [ sj.worldVertices[ sj.faces[i][0] ] ]; + var normal = sj.worldFaceNormals[i]; + + // Check how much the particle penetrates the polygon plane. + xi.vsub(verts[0],convexParticle_vertexToParticle); + var penetration = -normal.dot(convexParticle_vertexToParticle); + if(minPenetration===null || Math.abs(penetration) data.length || iMinY > data[0].length){ + return; + } + + // Clamp index to edges + if(iMinX < 0){ iMinX = 0; } + if(iMaxX < 0){ iMaxX = 0; } + if(iMinY < 0){ iMinY = 0; } + if(iMaxY < 0){ iMaxY = 0; } + if(iMinX >= data.length){ iMinX = data.length - 1; } + if(iMaxX >= data.length){ iMaxX = data.length - 1; } + if(iMaxY >= data[0].length){ iMaxY = data[0].length - 1; } + if(iMinY >= data[0].length){ iMinY = data[0].length - 1; } + + var minMax = []; + hfShape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax); + var min = minMax[0]; + var max = minMax[1]; + + // Bail out if we're cant touch the bounding height box + if(localConvexPos.z - radius > max || localConvexPos.z + radius < min){ + return; + } + + for(var i = iMinX; i < iMaxX; i++){ + for(var j = iMinY; j < iMaxY; j++){ + + // Lower triangle + hfShape.getConvexTrianglePillar(i, j, false); + Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); + if (convexPos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + convexShape.boundingSphereRadius) { + this.convexConvex(convexShape, hfShape.pillarConvex, convexPos, worldPillarOffset, convexQuat, hfQuat, convexBody, hfBody, null, null, faceList, null); + } + + // Upper triangle + hfShape.getConvexTrianglePillar(i, j, true); + Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); + if (convexPos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + convexShape.boundingSphereRadius) { + this.convexConvex(convexShape, hfShape.pillarConvex, convexPos, worldPillarOffset, convexQuat, hfQuat, convexBody, hfBody, null, null, faceList, null); + } + } + } +}; + +var sphereHeightfield_tmp1 = new Vec3(); +var sphereHeightfield_tmp2 = new Vec3(); + +/** + * @method sphereHeightfield + */ +Narrowphase.prototype[Shape.types.SPHERE | Shape.types.HEIGHTFIELD] = +Narrowphase.prototype.sphereHeightfield = function ( + sphereShape, + hfShape, + spherePos, + hfPos, + sphereQuat, + hfQuat, + sphereBody, + hfBody +){ + var data = hfShape.data, + radius = sphereShape.radius, + w = hfShape.elementSize, + worldPillarOffset = sphereHeightfield_tmp2; + + // Get sphere position to heightfield local! + var localSpherePos = sphereHeightfield_tmp1; + Transform.pointToLocalFrame(hfPos, hfQuat, spherePos, localSpherePos); + + // Get the index of the data points to test against + var iMinX = Math.floor((localSpherePos.x - radius) / w) - 1, + iMaxX = Math.ceil((localSpherePos.x + radius) / w) + 1, + iMinY = Math.floor((localSpherePos.y - radius) / w) - 1, + iMaxY = Math.ceil((localSpherePos.y + radius) / w) + 1; + + // Bail out if we are out of the terrain + if(iMaxX < 0 || iMaxY < 0 || iMinX > data.length || iMaxY > data[0].length){ + return; + } + + // Clamp index to edges + if(iMinX < 0){ iMinX = 0; } + if(iMaxX < 0){ iMaxX = 0; } + if(iMinY < 0){ iMinY = 0; } + if(iMaxY < 0){ iMaxY = 0; } + if(iMinX >= data.length){ iMinX = data.length - 1; } + if(iMaxX >= data.length){ iMaxX = data.length - 1; } + if(iMaxY >= data[0].length){ iMaxY = data[0].length - 1; } + if(iMinY >= data[0].length){ iMinY = data[0].length - 1; } + + var minMax = []; + hfShape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax); + var min = minMax[0]; + var max = minMax[1]; + + // Bail out if we're cant touch the bounding height box + if(localSpherePos.z - radius > max || localSpherePos.z + radius < min){ + return; + } + + var result = this.result; + for(var i = iMinX; i < iMaxX; i++){ + for(var j = iMinY; j < iMaxY; j++){ + + var numContactsBefore = result.length; + + // Lower triangle + hfShape.getConvexTrianglePillar(i, j, false); + Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); + if (spherePos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + sphereShape.boundingSphereRadius) { + this.sphereConvex(sphereShape, hfShape.pillarConvex, spherePos, worldPillarOffset, sphereQuat, hfQuat, sphereBody, hfBody); + } + + // Upper triangle + hfShape.getConvexTrianglePillar(i, j, true); + Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); + if (spherePos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + sphereShape.boundingSphereRadius) { + this.sphereConvex(sphereShape, hfShape.pillarConvex, spherePos, worldPillarOffset, sphereQuat, hfQuat, sphereBody, hfBody); + } + + var numContacts = result.length - numContactsBefore; + + if(numContacts > 2){ + return; + } + /* + // Skip all but 1 + for (var k = 0; k < numContacts - 1; k++) { + result.pop(); + } + */ + } + } +}; + +},{"../collision/AABB":3,"../collision/Ray":9,"../equations/ContactEquation":19,"../equations/FrictionEquation":21,"../math/Quaternion":28,"../math/Transform":29,"../math/Vec3":30,"../shapes/ConvexPolyhedron":38,"../shapes/Shape":43,"../solver/Solver":47,"../utils/Vec3Pool":54}],56:[function(_dereq_,module,exports){ +/* global performance */ + +module.exports = World; + +var Shape = _dereq_('../shapes/Shape'); +var Vec3 = _dereq_('../math/Vec3'); +var Quaternion = _dereq_('../math/Quaternion'); +var GSSolver = _dereq_('../solver/GSSolver'); +var Vec3Pool = _dereq_('../utils/Vec3Pool'); +var ContactEquation = _dereq_('../equations/ContactEquation'); +var FrictionEquation = _dereq_('../equations/FrictionEquation'); +var Narrowphase = _dereq_('./Narrowphase'); +var EventTarget = _dereq_('../utils/EventTarget'); +var ArrayCollisionMatrix = _dereq_('../collision/ArrayCollisionMatrix'); +var Material = _dereq_('../material/Material'); +var ContactMaterial = _dereq_('../material/ContactMaterial'); +var Body = _dereq_('../objects/Body'); +var TupleDictionary = _dereq_('../utils/TupleDictionary'); +var RaycastResult = _dereq_('../collision/RaycastResult'); +var AABB = _dereq_('../collision/AABB'); +var Ray = _dereq_('../collision/Ray'); +var NaiveBroadphase = _dereq_('../collision/NaiveBroadphase'); + +/** + * The physics world + * @class World + * @constructor + * @extends EventTarget + */ +function World(){ + EventTarget.apply(this); + + /** + * Currently / last used timestep. Is set to -1 if not available. This value is updated before each internal step, which means that it is "fresh" inside event callbacks. + * @property {Number} dt + */ + this.dt = -1; + + /** + * Makes bodies go to sleep when they've been inactive + * @property allowSleep + * @type {Boolean} + */ + this.allowSleep = false; + + /** + * All the current contacts (instances of ContactEquation) in the world. + * @property contacts + * @type {Array} + */ + this.contacts = []; + this.frictionEquations = []; + + /** + * How often to normalize quaternions. Set to 0 for every step, 1 for every second etc.. A larger value increases performance. If bodies tend to explode, set to a smaller value (zero to be sure nothing can go wrong). + * @property quatNormalizeSkip + * @type {Number} + */ + this.quatNormalizeSkip = 0; + + /** + * Set to true to use fast quaternion normalization. It is often enough accurate to use. If bodies tend to explode, set to false. + * @property quatNormalizeFast + * @type {Boolean} + * @see Quaternion.normalizeFast + * @see Quaternion.normalize + */ + this.quatNormalizeFast = false; + + /** + * The wall-clock time since simulation start + * @property time + * @type {Number} + */ + this.time = 0.0; + + /** + * Number of timesteps taken since start + * @property stepnumber + * @type {Number} + */ + this.stepnumber = 0; + + /// Default and last timestep sizes + this.default_dt = 1/60; + + this.nextId = 0; + /** + * @property gravity + * @type {Vec3} + */ + this.gravity = new Vec3(); + + /** + * @property broadphase + * @type {Broadphase} + */ + this.broadphase = new NaiveBroadphase(); + + /** + * @property bodies + * @type {Array} + */ + this.bodies = []; + + /** + * @property solver + * @type {Solver} + */ + this.solver = new GSSolver(); + + /** + * @property constraints + * @type {Array} + */ + this.constraints = []; + + /** + * @property narrowphase + * @type {Narrowphase} + */ + this.narrowphase = new Narrowphase(this); + + /** + * @property {ArrayCollisionMatrix} collisionMatrix + * @type {ArrayCollisionMatrix} + */ + this.collisionMatrix = new ArrayCollisionMatrix(); + + /** + * CollisionMatrix from the previous step. + * @property {ArrayCollisionMatrix} collisionMatrixPrevious + * @type {ArrayCollisionMatrix} + */ + this.collisionMatrixPrevious = new ArrayCollisionMatrix(); + + /** + * All added materials + * @property materials + * @type {Array} + */ + this.materials = []; + + /** + * @property contactmaterials + * @type {Array} + */ + this.contactmaterials = []; + + /** + * Used to look up a ContactMaterial given two instances of Material. + * @property {TupleDictionary} contactMaterialTable + */ + this.contactMaterialTable = new TupleDictionary(); + + this.defaultMaterial = new Material("default"); + + /** + * This contact material is used if no suitable contactmaterial is found for a contact. + * @property defaultContactMaterial + * @type {ContactMaterial} + */ + this.defaultContactMaterial = new ContactMaterial(this.defaultMaterial, this.defaultMaterial, { friction: 0.3, restitution: 0.0 }); + + /** + * @property doProfiling + * @type {Boolean} + */ + this.doProfiling = false; + + /** + * @property profile + * @type {Object} + */ + this.profile = { + solve:0, + makeContactConstraints:0, + broadphase:0, + integrate:0, + narrowphase:0, + }; + + /** + * @property subsystems + * @type {Array} + */ + this.subsystems = []; + + this.addBodyEvent = { + type:"addBody", + body : null, + }; + + this.removeBodyEvent = { + type:"removeBody", + body : null, + }; +} +World.prototype = new EventTarget(); + +// Temp stuff +var tmpAABB1 = new AABB(); +var tmpArray1 = []; +var tmpRay = new Ray(); + +/** + * Get the contact material between materials m1 and m2 + * @method getContactMaterial + * @param {Material} m1 + * @param {Material} m2 + * @return {ContactMaterial} The contact material if it was found. + */ +World.prototype.getContactMaterial = function(m1,m2){ + return this.contactMaterialTable.get(m1.id,m2.id); //this.contactmaterials[this.mats2cmat[i+j*this.materials.length]]; +}; + +/** + * Get number of objects in the world. + * @method numObjects + * @return {Number} + * @deprecated + */ +World.prototype.numObjects = function(){ + return this.bodies.length; +}; + +/** + * Store old collision state info + * @method collisionMatrixTick + */ +World.prototype.collisionMatrixTick = function(){ + var temp = this.collisionMatrixPrevious; + this.collisionMatrixPrevious = this.collisionMatrix; + this.collisionMatrix = temp; + this.collisionMatrix.reset(); +}; + +/** + * Add a rigid body to the simulation. + * @method add + * @param {Body} body + * @todo If the simulation has not yet started, why recrete and copy arrays for each body? Accumulate in dynamic arrays in this case. + * @todo Adding an array of bodies should be possible. This would save some loops too + * @deprecated Use .addBody instead + */ +World.prototype.add = World.prototype.addBody = function(body){ + if(this.bodies.indexOf(body) !== -1){ + return; + } + body.index = this.bodies.length; + this.bodies.push(body); + body.world = this; + body.initPosition.copy(body.position); + body.initVelocity.copy(body.velocity); + body.timeLastSleepy = this.time; + if(body instanceof Body){ + body.initAngularVelocity.copy(body.angularVelocity); + body.initQuaternion.copy(body.quaternion); + } + this.collisionMatrix.setNumObjects(this.bodies.length); + this.addBodyEvent.body = body; + this.dispatchEvent(this.addBodyEvent); +}; + +/** + * Add a constraint to the simulation. + * @method addConstraint + * @param {Constraint} c + */ +World.prototype.addConstraint = function(c){ + this.constraints.push(c); +}; + +/** + * Removes a constraint + * @method removeConstraint + * @param {Constraint} c + */ +World.prototype.removeConstraint = function(c){ + var idx = this.constraints.indexOf(c); + if(idx!==-1){ + this.constraints.splice(idx,1); + } +}; + +/** + * Raycast test + * @method rayTest + * @param {Vec3} from + * @param {Vec3} to + * @param {Function|RaycastResult} result + * @deprecated Use .raycastAll, .raycastClosest or .raycastAny instead. + */ +World.prototype.rayTest = function(from, to, result){ + if(result instanceof RaycastResult){ + // Do raycastclosest + this.raycastClosest(from, to, { + skipBackfaces: true + }, result); + } else { + // Do raycastAll + this.raycastAll(from, to, { + skipBackfaces: true + }, result); + } +}; + +/** + * Ray cast against all bodies. The provided callback will be executed for each hit with a RaycastResult as single argument. + * @method raycastAll + * @param {Vec3} from + * @param {Vec3} to + * @param {Object} options + * @param {number} [options.collisionFilterMask=-1] + * @param {number} [options.collisionFilterGroup=-1] + * @param {boolean} [options.skipBackfaces=false] + * @param {boolean} [options.checkCollisionResponse=true] + * @param {Function} callback + * @return {boolean} True if any body was hit. + */ +World.prototype.raycastAll = function(from, to, options, callback){ + options.mode = Ray.ALL; + options.from = from; + options.to = to; + options.callback = callback; + return tmpRay.intersectWorld(this, options); +}; + +/** + * Ray cast, and stop at the first result. Note that the order is random - but the method is fast. + * @method raycastAny + * @param {Vec3} from + * @param {Vec3} to + * @param {Object} options + * @param {number} [options.collisionFilterMask=-1] + * @param {number} [options.collisionFilterGroup=-1] + * @param {boolean} [options.skipBackfaces=false] + * @param {boolean} [options.checkCollisionResponse=true] + * @param {RaycastResult} result + * @return {boolean} True if any body was hit. + */ +World.prototype.raycastAny = function(from, to, options, result){ + options.mode = Ray.ANY; + options.from = from; + options.to = to; + options.result = result; + return tmpRay.intersectWorld(this, options); +}; + +/** + * Ray cast, and return information of the closest hit. + * @method raycastClosest + * @param {Vec3} from + * @param {Vec3} to + * @param {Object} options + * @param {number} [options.collisionFilterMask=-1] + * @param {number} [options.collisionFilterGroup=-1] + * @param {boolean} [options.skipBackfaces=false] + * @param {boolean} [options.checkCollisionResponse=true] + * @param {RaycastResult} result + * @return {boolean} True if any body was hit. + */ +World.prototype.raycastClosest = function(from, to, options, result){ + options.mode = Ray.CLOSEST; + options.from = from; + options.to = to; + options.result = result; + return tmpRay.intersectWorld(this, options); +}; + +/** + * Remove a rigid body from the simulation. + * @method remove + * @param {Body} body + * @deprecated Use .removeBody instead + */ +World.prototype.remove = function(body){ + body.world = null; + var n = this.bodies.length-1, + bodies = this.bodies, + idx = bodies.indexOf(body); + if(idx !== -1){ + bodies.splice(idx, 1); // Todo: should use a garbage free method + + // Recompute index + for(var i=0; i!==bodies.length; i++){ + bodies[i].index = i; + } + + this.collisionMatrix.setNumObjects(n); + this.removeBodyEvent.body = body; + this.dispatchEvent(this.removeBodyEvent); + } +}; + +/** + * Remove a rigid body from the simulation. + * @method removeBody + * @param {Body} body + */ +World.prototype.removeBody = World.prototype.remove; + +/** + * Adds a material to the World. + * @method addMaterial + * @param {Material} m + * @todo Necessary? + */ +World.prototype.addMaterial = function(m){ + this.materials.push(m); +}; + +/** + * Adds a contact material to the World + * @method addContactMaterial + * @param {ContactMaterial} cmat + */ +World.prototype.addContactMaterial = function(cmat) { + + // Add contact material + this.contactmaterials.push(cmat); + + // Add current contact material to the material table + this.contactMaterialTable.set(cmat.materials[0].id,cmat.materials[1].id,cmat); +}; + +// performance.now() +if(typeof performance === 'undefined'){ + performance = {}; +} +if(!performance.now){ + var nowOffset = Date.now(); + if (performance.timing && performance.timing.navigationStart){ + nowOffset = performance.timing.navigationStart; + } + performance.now = function(){ + return Date.now() - nowOffset; + }; +} + +var step_tmp1 = new Vec3(); + +/** + * Step the physics world forward in time. + * + * There are two modes. The simple mode is fixed timestepping without interpolation. In this case you only use the first argument. The second case uses interpolation. In that you also provide the time since the function was last used, as well as the maximum fixed timesteps to take. + * + * @method step + * @param {Number} dt The fixed time step size to use. + * @param {Number} [timeSinceLastCalled] The time elapsed since the function was last called. + * @param {Number} [maxSubSteps=10] Maximum number of fixed steps to take per function call. + * + * @example + * // fixed timestepping without interpolation + * world.step(1/60); + * + * @see http://bulletphysics.org/mediawiki-1.5.8/index.php/Stepping_The_World + */ +World.prototype.step = function(dt, timeSinceLastCalled, maxSubSteps){ + maxSubSteps = maxSubSteps || 10; + timeSinceLastCalled = timeSinceLastCalled || 0; + + if(timeSinceLastCalled === 0){ // Fixed, simple stepping + + this.internalStep(dt); + + // Increment time + this.time += dt; + + } else { + + // Compute the number of fixed steps we should have taken since the last step + var internalSteps = Math.floor((this.time + timeSinceLastCalled) / dt) - Math.floor(this.time / dt); + internalSteps = Math.min(internalSteps,maxSubSteps); + + // Do some fixed steps to catch up + var t0 = performance.now(); + for(var i=0; i!==internalSteps; i++){ + this.internalStep(dt); + if(performance.now() - t0 > dt * 1000){ + // We are slower than real-time. Better bail out. + break; + } + } + + // Increment internal clock + this.time += timeSinceLastCalled; + + // Compute "Left over" time step + var h = this.time % dt; + var h_div_dt = h / dt; + var interpvelo = step_tmp1; + var bodies = this.bodies; + + for(var j=0; j !== bodies.length; j++){ + var b = bodies[j]; + if(b.type !== Body.STATIC && b.sleepState !== Body.SLEEPING){ + + // Interpolate + b.position.vsub(b.previousPosition, interpvelo); + interpvelo.scale(h_div_dt, interpvelo); + b.position.vadd(interpvelo, b.interpolatedPosition); + + // TODO: interpolate quaternion + // b.interpolatedAngle = b.angle + (b.angle - b.previousAngle) * h_div_dt; + + } else { + + // For static bodies, just copy. Who else will do it? + b.interpolatedPosition.copy(b.position); + b.interpolatedQuaternion.copy(b.quaternion); + } + } + } +}; + +/** + * Step the simulation + * @method step + * @param {Number} dt + */ +var World_step_postStepEvent = {type:"postStep"}, // Reusable event objects to save memory + World_step_preStepEvent = {type:"preStep"}, + World_step_collideEvent = {type:"collide", body:null, contact:null }, + World_step_oldContacts = [], // Pools for unused objects + World_step_frictionEquationPool = [], + World_step_p1 = [], // Reusable arrays for collision pairs + World_step_p2 = [], + World_step_gvec = new Vec3(), // Temporary vectors and quats + World_step_vi = new Vec3(), + World_step_vj = new Vec3(), + World_step_wi = new Vec3(), + World_step_wj = new Vec3(), + World_step_t1 = new Vec3(), + World_step_t2 = new Vec3(), + World_step_rixn = new Vec3(), + World_step_rjxn = new Vec3(), + World_step_step_q = new Quaternion(), + World_step_step_w = new Quaternion(), + World_step_step_wq = new Quaternion(), + invI_tau_dt = new Vec3(); +World.prototype.internalStep = function(dt){ + this.dt = dt; + + var world = this, + that = this, + contacts = this.contacts, + p1 = World_step_p1, + p2 = World_step_p2, + N = this.numObjects(), + bodies = this.bodies, + solver = this.solver, + gravity = this.gravity, + doProfiling = this.doProfiling, + profile = this.profile, + DYNAMIC = Body.DYNAMIC, + profilingStart, + constraints = this.constraints, + frictionEquationPool = World_step_frictionEquationPool, + gnorm = gravity.norm(), + gx = gravity.x, + gy = gravity.y, + gz = gravity.z, + i=0; + + if(doProfiling){ + profilingStart = performance.now(); + } + + // Add gravity to all objects + for(i=0; i!==N; i++){ + var bi = bodies[i]; + if(bi.type & DYNAMIC){ // Only for dynamic bodies + var f = bi.force, m = bi.mass; + f.x += m*gx; + f.y += m*gy; + f.z += m*gz; + } + } + + // Update subsystems + for(var i=0, Nsubsystems=this.subsystems.length; i!==Nsubsystems; i++){ + this.subsystems[i].update(); + } + + // Collision detection + if(doProfiling){ profilingStart = performance.now(); } + p1.length = 0; // Clean up pair arrays from last step + p2.length = 0; + this.broadphase.collisionPairs(this,p1,p2); + if(doProfiling){ profile.broadphase = performance.now() - profilingStart; } + + // Remove constrained pairs with collideConnected == false + var Nconstraints = constraints.length; + for(i=0; i!==Nconstraints; i++){ + var c = constraints[i]; + if(!c.collideConnected){ + for(var j = p1.length-1; j>=0; j-=1){ + if( (c.bodyA === p1[j] && c.bodyB === p2[j]) || + (c.bodyB === p1[j] && c.bodyA === p2[j])){ + p1.splice(j, 1); + p2.splice(j, 1); + } + } + } + } + + this.collisionMatrixTick(); + + // Generate contacts + if(doProfiling){ profilingStart = performance.now(); } + var oldcontacts = World_step_oldContacts; + var NoldContacts = contacts.length; + + for(i=0; i!==NoldContacts; i++){ + oldcontacts.push(contacts[i]); + } + contacts.length = 0; + + // Transfer FrictionEquation from current list to the pool for reuse + var NoldFrictionEquations = this.frictionEquations.length; + for(i=0; i!==NoldFrictionEquations; i++){ + frictionEquationPool.push(this.frictionEquations[i]); + } + this.frictionEquations.length = 0; + + this.narrowphase.getContacts( + p1, + p2, + this, + contacts, + oldcontacts, // To be reused + this.frictionEquations, + frictionEquationPool + ); + + if(doProfiling){ + profile.narrowphase = performance.now() - profilingStart; + } + + // Loop over all collisions + if(doProfiling){ + profilingStart = performance.now(); + } + + // Add all friction eqs + for (var i = 0; i < this.frictionEquations.length; i++) { + solver.addEquation(this.frictionEquations[i]); + } + + var ncontacts = contacts.length; + for(var k=0; k!==ncontacts; k++){ + + // Current contact + var c = contacts[k]; + + // Get current collision indeces + var bi = c.bi, + bj = c.bj, + si = c.si, + sj = c.sj; + + // Get collision properties + var cm; + if(bi.material && bj.material){ + cm = this.getContactMaterial(bi.material,bj.material) || this.defaultContactMaterial; + } else { + cm = this.defaultContactMaterial; + } + + // c.enabled = bi.collisionResponse && bj.collisionResponse && si.collisionResponse && sj.collisionResponse; + + var mu = cm.friction; + // c.restitution = cm.restitution; + + // If friction or restitution were specified in the material, use them + if(bi.material && bj.material){ + if(bi.material.friction >= 0 && bj.material.friction >= 0){ + mu = bi.material.friction * bj.material.friction; + } + + if(bi.material.restitution >= 0 && bj.material.restitution >= 0){ + c.restitution = bi.material.restitution * bj.material.restitution; + } + } + + // c.setSpookParams( + // cm.contactEquationStiffness, + // cm.contactEquationRelaxation, + // dt + // ); + + solver.addEquation(c); + + // // Add friction constraint equation + // if(mu > 0){ + + // // Create 2 tangent equations + // var mug = mu * gnorm; + // var reducedMass = (bi.invMass + bj.invMass); + // if(reducedMass > 0){ + // reducedMass = 1/reducedMass; + // } + // var pool = frictionEquationPool; + // var c1 = pool.length ? pool.pop() : new FrictionEquation(bi,bj,mug*reducedMass); + // var c2 = pool.length ? pool.pop() : new FrictionEquation(bi,bj,mug*reducedMass); + // this.frictionEquations.push(c1, c2); + + // c1.bi = c2.bi = bi; + // c1.bj = c2.bj = bj; + // c1.minForce = c2.minForce = -mug*reducedMass; + // c1.maxForce = c2.maxForce = mug*reducedMass; + + // // Copy over the relative vectors + // c1.ri.copy(c.ri); + // c1.rj.copy(c.rj); + // c2.ri.copy(c.ri); + // c2.rj.copy(c.rj); + + // // Construct tangents + // c.ni.tangents(c1.t, c2.t); + + // // Set spook params + // c1.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, dt); + // c2.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, dt); + + // c1.enabled = c2.enabled = c.enabled; + + // // Add equations to solver + // solver.addEquation(c1); + // solver.addEquation(c2); + // } + + if( bi.allowSleep && + bi.type === Body.DYNAMIC && + bi.sleepState === Body.SLEEPING && + bj.sleepState === Body.AWAKE && + bj.type !== Body.STATIC + ){ + var speedSquaredB = bj.velocity.norm2() + bj.angularVelocity.norm2(); + var speedLimitSquaredB = Math.pow(bj.sleepSpeedLimit,2); + if(speedSquaredB >= speedLimitSquaredB*2){ + bi._wakeUpAfterNarrowphase = true; + } + } + + if( bj.allowSleep && + bj.type === Body.DYNAMIC && + bj.sleepState === Body.SLEEPING && + bi.sleepState === Body.AWAKE && + bi.type !== Body.STATIC + ){ + var speedSquaredA = bi.velocity.norm2() + bi.angularVelocity.norm2(); + var speedLimitSquaredA = Math.pow(bi.sleepSpeedLimit,2); + if(speedSquaredA >= speedLimitSquaredA*2){ + bj._wakeUpAfterNarrowphase = true; + } + } + + // Now we know that i and j are in contact. Set collision matrix state + this.collisionMatrix.set(bi, bj, true); + + if (!this.collisionMatrixPrevious.get(bi, bj)) { + // First contact! + // We reuse the collideEvent object, otherwise we will end up creating new objects for each new contact, even if there's no event listener attached. + World_step_collideEvent.body = bj; + World_step_collideEvent.contact = c; + bi.dispatchEvent(World_step_collideEvent); + + World_step_collideEvent.body = bi; + bj.dispatchEvent(World_step_collideEvent); + } + } + if(doProfiling){ + profile.makeContactConstraints = performance.now() - profilingStart; + profilingStart = performance.now(); + } + + // Wake up bodies + for(i=0; i!==N; i++){ + var bi = bodies[i]; + if(bi._wakeUpAfterNarrowphase){ + bi.wakeUp(); + bi._wakeUpAfterNarrowphase = false; + } + } + + // Add user-added constraints + var Nconstraints = constraints.length; + for(i=0; i!==Nconstraints; i++){ + var c = constraints[i]; + c.update(); + for(var j=0, Neq=c.equations.length; j!==Neq; j++){ + var eq = c.equations[j]; + solver.addEquation(eq); + } + } + + // Solve the constrained system + solver.solve(dt,this); + + if(doProfiling){ + profile.solve = performance.now() - profilingStart; + } + + // Remove all contacts from solver + solver.removeAllEquations(); + + // Apply damping, see http://code.google.com/p/bullet/issues/detail?id=74 for details + var pow = Math.pow; + for(i=0; i!==N; i++){ + var bi = bodies[i]; + if(bi.type & DYNAMIC){ // Only for dynamic bodies + var ld = pow(1.0 - bi.linearDamping,dt); + var v = bi.velocity; + v.mult(ld,v); + var av = bi.angularVelocity; + if(av){ + var ad = pow(1.0 - bi.angularDamping,dt); + av.mult(ad,av); + } + } + } + + this.dispatchEvent(World_step_preStepEvent); + + // Invoke pre-step callbacks + for(i=0; i!==N; i++){ + var bi = bodies[i]; + if(bi.preStep){ + bi.preStep.call(bi); + } + } + + // Leap frog + // vnew = v + h*f/m + // xnew = x + h*vnew + if(doProfiling){ + profilingStart = performance.now(); + } + var q = World_step_step_q; + var w = World_step_step_w; + var wq = World_step_step_wq; + var stepnumber = this.stepnumber; + var DYNAMIC_OR_KINEMATIC = Body.DYNAMIC | Body.KINEMATIC; + var quatNormalize = stepnumber % (this.quatNormalizeSkip+1) === 0; + var quatNormalizeFast = this.quatNormalizeFast; + var half_dt = dt * 0.5; + var PLANE = Shape.types.PLANE, + CONVEX = Shape.types.CONVEXPOLYHEDRON; + + for(i=0; i!==N; i++){ + var b = bodies[i], + force = b.force, + tau = b.torque; + if((b.type & DYNAMIC_OR_KINEMATIC) && b.sleepState !== Body.SLEEPING){ // Only for dynamic + var velo = b.velocity, + angularVelo = b.angularVelocity, + pos = b.position, + quat = b.quaternion, + invMass = b.invMass, + invInertia = b.invInertiaWorld; + + velo.x += force.x * invMass * dt; + velo.y += force.y * invMass * dt; + velo.z += force.z * invMass * dt; + + if(b.angularVelocity){ + invInertia.vmult(tau,invI_tau_dt); + invI_tau_dt.mult(dt,invI_tau_dt); + invI_tau_dt.vadd(angularVelo,angularVelo); + } + + // Use new velocity - leap frog + pos.x += velo.x * dt; + pos.y += velo.y * dt; + pos.z += velo.z * dt; + + if(b.angularVelocity){ + w.set(angularVelo.x, angularVelo.y, angularVelo.z, 0); + w.mult(quat,wq); + quat.x += half_dt * wq.x; + quat.y += half_dt * wq.y; + quat.z += half_dt * wq.z; + quat.w += half_dt * wq.w; + if(quatNormalize){ + if(quatNormalizeFast){ + quat.normalizeFast(); + } else { + quat.normalize(); + } + } + } + + if(b.aabb){ + b.aabbNeedsUpdate = true; + } + + // Update world inertia + if(b.updateInertiaWorld){ + b.updateInertiaWorld(); + } + } + } + this.clearForces(); + + this.broadphase.dirty = true; + + if(doProfiling){ + profile.integrate = performance.now() - profilingStart; + } + + // Update world time + this.time += dt; + this.stepnumber += 1; + + this.dispatchEvent(World_step_postStepEvent); + + // Invoke post-step callbacks + for(i=0; i!==N; i++){ + var bi = bodies[i]; + var postStep = bi.postStep; + if(postStep){ + postStep.call(bi); + } + } + + // Sleeping update + if(this.allowSleep){ + for(i=0; i!==N; i++){ + bodies[i].sleepTick(this.time); + } + } +}; + +/** + * Sets all body forces in the world to zero. + * @method clearForces + */ +World.prototype.clearForces = function(){ + var bodies = this.bodies; + var N = bodies.length; + for(var i=0; i !== N; i++){ + var b = bodies[i], + force = b.force, + tau = b.torque; + + b.force.set(0,0,0); + b.torque.set(0,0,0); + } +}; + +},{"../collision/AABB":3,"../collision/ArrayCollisionMatrix":4,"../collision/NaiveBroadphase":7,"../collision/Ray":9,"../collision/RaycastResult":10,"../equations/ContactEquation":19,"../equations/FrictionEquation":21,"../material/ContactMaterial":24,"../material/Material":25,"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"../shapes/Shape":43,"../solver/GSSolver":46,"../utils/EventTarget":49,"../utils/TupleDictionary":52,"../utils/Vec3Pool":54,"./Narrowphase":55}]},{},[2]) +(2) +}); \ No newline at end of file diff --git a/daydream/public/libs/cannon.min.js b/daydream/public/libs/cannon.min.js new file mode 100644 index 000000000..01a53bc6d --- /dev/null +++ b/daydream/public/libs/cannon.min.js @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2015 cannon.js Authors + * + * 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(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&false)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.CANNON=e()}}(function(){return function e(f,n,o){function d(t,l){if(!n[t]){if(!f[t]){var u="function"==typeof require&&require;if(!l&&u)return u(t,!0);if(i)return i(t,!0);throw new Error("Cannot find module '"+t+"'")}var p=n[t]={exports:{}};f[t][0].call(p.exports,function(e){var n=f[t][1][e];return d(n?n:e)},p,p.exports,e,f,n,o)}return n[t].exports}for(var i="function"==typeof require&&require,t=0;t (http://steffe.se)",keywords:["cannon.js","cannon","physics","engine","3d"],main:"./build/cannon.js",engines:{node:"*"},repository:{type:"git",url:"https://github.com/schteppe/cannon.js.git"},bugs:{url:"https://github.com/schteppe/cannon.js/issues"},licenses:[{type:"MIT"}],devDependencies:{jshint:"latest","uglify-js":"latest",nodeunit:"^0.9.0",grunt:"~0.4.0","grunt-contrib-jshint":"~0.1.1","grunt-contrib-nodeunit":"^0.4.1","grunt-contrib-concat":"~0.1.3","grunt-contrib-uglify":"^0.5.1","grunt-browserify":"^2.1.4","grunt-contrib-yuidoc":"^0.5.2",browserify:"*"},dependencies:{}}},{}],2:[function(e,f){f.exports={version:e("../package.json").version,AABB:e("./collision/AABB"),ArrayCollisionMatrix:e("./collision/ArrayCollisionMatrix"),Body:e("./objects/Body"),Box:e("./shapes/Box"),Broadphase:e("./collision/Broadphase"),Constraint:e("./constraints/Constraint"),ContactEquation:e("./equations/ContactEquation"),Narrowphase:e("./world/Narrowphase"),ConeTwistConstraint:e("./constraints/ConeTwistConstraint"),ContactMaterial:e("./material/ContactMaterial"),ConvexPolyhedron:e("./shapes/ConvexPolyhedron"),Cylinder:e("./shapes/Cylinder"),DistanceConstraint:e("./constraints/DistanceConstraint"),Equation:e("./equations/Equation"),EventTarget:e("./utils/EventTarget"),FrictionEquation:e("./equations/FrictionEquation"),GSSolver:e("./solver/GSSolver"),GridBroadphase:e("./collision/GridBroadphase"),Heightfield:e("./shapes/Heightfield"),HingeConstraint:e("./constraints/HingeConstraint"),LockConstraint:e("./constraints/LockConstraint"),Mat3:e("./math/Mat3"),Material:e("./material/Material"),NaiveBroadphase:e("./collision/NaiveBroadphase"),ObjectCollisionMatrix:e("./collision/ObjectCollisionMatrix"),Pool:e("./utils/Pool"),Particle:e("./shapes/Particle"),Plane:e("./shapes/Plane"),PointToPointConstraint:e("./constraints/PointToPointConstraint"),Quaternion:e("./math/Quaternion"),Ray:e("./collision/Ray"),RaycastVehicle:e("./objects/RaycastVehicle"),RaycastResult:e("./collision/RaycastResult"),RigidVehicle:e("./objects/RigidVehicle"),RotationalEquation:e("./equations/RotationalEquation"),RotationalMotorEquation:e("./equations/RotationalMotorEquation"),SAPBroadphase:e("./collision/SAPBroadphase"),SPHSystem:e("./objects/SPHSystem"),Shape:e("./shapes/Shape"),Solver:e("./solver/Solver"),Sphere:e("./shapes/Sphere"),SplitSolver:e("./solver/SplitSolver"),Spring:e("./objects/Spring"),Trimesh:e("./shapes/Trimesh"),Vec3:e("./math/Vec3"),Vec3Pool:e("./utils/Vec3Pool"),World:e("./world/World")}},{"../package.json":1,"./collision/AABB":3,"./collision/ArrayCollisionMatrix":4,"./collision/Broadphase":5,"./collision/GridBroadphase":6,"./collision/NaiveBroadphase":7,"./collision/ObjectCollisionMatrix":8,"./collision/Ray":9,"./collision/RaycastResult":10,"./collision/SAPBroadphase":11,"./constraints/ConeTwistConstraint":12,"./constraints/Constraint":13,"./constraints/DistanceConstraint":14,"./constraints/HingeConstraint":15,"./constraints/LockConstraint":16,"./constraints/PointToPointConstraint":17,"./equations/ContactEquation":19,"./equations/Equation":20,"./equations/FrictionEquation":21,"./equations/RotationalEquation":22,"./equations/RotationalMotorEquation":23,"./material/ContactMaterial":24,"./material/Material":25,"./math/Mat3":27,"./math/Quaternion":28,"./math/Vec3":30,"./objects/Body":31,"./objects/RaycastVehicle":32,"./objects/RigidVehicle":33,"./objects/SPHSystem":34,"./objects/Spring":35,"./shapes/Box":37,"./shapes/ConvexPolyhedron":38,"./shapes/Cylinder":39,"./shapes/Heightfield":40,"./shapes/Particle":41,"./shapes/Plane":42,"./shapes/Shape":43,"./shapes/Sphere":44,"./shapes/Trimesh":45,"./solver/GSSolver":46,"./solver/Solver":47,"./solver/SplitSolver":48,"./utils/EventTarget":49,"./utils/Pool":51,"./utils/Vec3Pool":54,"./world/Narrowphase":55,"./world/World":56}],3:[function(e,f){function n(e){e=e||{},this.lowerBound=new o,e.lowerBound&&this.lowerBound.copy(e.lowerBound),this.upperBound=new o,e.upperBound&&this.upperBound.copy(e.upperBound)}{var o=e("../math/Vec3");e("../utils/Utils")}f.exports=n;var d=new o;n.prototype.setFromPoints=function(e,f,n,o){var i=this.lowerBound,t=this.upperBound,l=n;i.copy(e[0]),l&&l.vmult(i,i),t.copy(i);for(var u=1;ut.x&&(t.x=p.x),p.xt.y&&(t.y=p.y),p.yt.z&&(t.z=p.z),p.zf&&(this.lowerBound.x=f);var n=e.upperBound.x;this.upperBound.xf&&(this.lowerBound.y=f);var n=e.upperBound.y;this.upperBound.yf&&(this.lowerBound.z=f);var n=e.upperBound.z;this.upperBound.z=d.x&&f.y<=o.y&&n.y>=d.y&&f.z<=o.z&&n.z>=d.z},n.prototype.getCorners=function(e,f,n,o,d,i,t,l){var u=this.lowerBound,p=this.upperBound;e.copy(u),f.set(p.x,u.y,u.z),n.set(p.x,p.y,u.z),o.set(u.x,p.y,p.z),d.set(p.x,u.y,u.z),i.set(u.x,p.y,u.z),t.set(u.x,u.y,p.z),l.copy(p)};var i=[new o,new o,new o,new o,new o,new o,new o,new o];n.prototype.toLocalFrame=function(e,f){var n=i,o=n[0],d=n[1],t=n[2],l=n[3],u=n[4],p=n[5],s=n[6],y=n[7];this.getCorners(o,d,t,l,u,p,s,y);for(var c=0;8!==c;c++){var a=n[c];e.pointToLocal(a,a)}return f.setFromPoints(n)},n.prototype.toWorldFrame=function(e,f){var n=i,o=n[0],d=n[1],t=n[2],l=n[3],u=n[4],p=n[5],s=n[6],y=n[7];this.getCorners(o,d,t,l,u,p,s,y);for(var c=0;8!==c;c++){var a=n[c];e.pointToWorld(a,a)}return f.setFromPoints(n)}},{"../math/Vec3":30,"../utils/Utils":53}],4:[function(e,f){function n(){this.matrix=[]}f.exports=n,n.prototype.get=function(e,f){if(e=e.index,f=f.index,f>e){var n=f;f=e,e=n}return this.matrix[(e*(e+1)>>1)+f-1]},n.prototype.set=function(e,f,n){if(e=e.index,f=f.index,f>e){var o=f;f=e,e=o}this.matrix[(e*(e+1)>>1)+f-1]=n?1:0},n.prototype.reset=function(){for(var e=0,f=this.matrix.length;e!==f;e++)this.matrix[e]=0},n.prototype.setNumObjects=function(e){this.matrix.length=e*(e-1)>>1}},{}],5:[function(e,f){function n(){this.world=null,this.useBoundingBoxes=!1,this.dirty=!0}{var o=e("../objects/Body"),d=e("../math/Vec3"),i=e("../math/Quaternion");e("../shapes/Shape"),e("../shapes/Plane")}f.exports=n,n.prototype.collisionPairs=function(){throw new Error("collisionPairs not implemented for this BroadPhase class!")};var t=o.STATIC|o.KINEMATIC;n.prototype.needBroadphaseCollision=function(e,f){return 0===(e.collisionFilterGroup&f.collisionFilterMask)||0===(f.collisionFilterGroup&e.collisionFilterMask)?!1:0===(e.type&t)&&e.sleepState!==o.SLEEPING||0===(f.type&t)&&f.sleepState!==o.SLEEPING?!0:!1},n.prototype.intersectionTest=function(e,f,n,o){this.useBoundingBoxes?this.doBoundingBoxBroadphase(e,f,n,o):this.doBoundingSphereBroadphase(e,f,n,o)};{var l=new d;new d,new i,new d}n.prototype.doBoundingSphereBroadphase=function(e,f,n,o){var d=l;f.position.vsub(e.position,d);var i=Math.pow(e.boundingRadius+f.boundingRadius,2),t=d.norm2();i>t&&(n.push(e),o.push(f))},n.prototype.doBoundingBoxBroadphase=function(e,f,n,o){e.aabbNeedsUpdate&&e.computeAABB(),f.aabbNeedsUpdate&&f.computeAABB(),e.aabb.overlaps(f.aabb)&&(n.push(e),o.push(f))};var u={keys:[]},p=[],s=[];n.prototype.makePairsUnique=function(e,f){for(var n=u,o=p,d=s,i=e.length,t=0;t!==i;t++)o[t]=e[t],d[t]=f[t];e.length=0,f.length=0;for(var t=0;t!==i;t++){var l=o[t].id,y=d[t].id,c=y>l?l+","+y:y+","+l;n[c]=t,n.keys.push(c)}for(var t=0;t!==n.keys.length;t++){var c=n.keys.pop(),a=n[c];e.push(o[a]),f.push(d[a]),delete n[c]}},n.prototype.setWorld=function(){};var y=new d;n.boundingSphereCheck=function(e,f){var n=y;return e.position.vsub(f.position,n),Math.pow(e.shape.boundingSphereRadius+f.shape.boundingSphereRadius,2)>n.norm2()},n.prototype.aabbQuery=function(){return console.warn(".aabbQuery is not implemented in this Broadphase subclass."),[]}},{"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"../shapes/Plane":42,"../shapes/Shape":43}],6:[function(e,f){function n(e,f,n,i,t){o.apply(this),this.nx=n||10,this.ny=i||10,this.nz=t||10,this.aabbMin=e||new d(100,100,100),this.aabbMax=f||new d(-100,-100,-100);var l=this.nx*this.ny*this.nz;if(0>=l)throw"GridBroadphase: Each dimension's n must be >0";this.bins=[],this.binLengths=[],this.bins.length=l,this.binLengths.length=l;for(var u=0;l>u;u++)this.bins[u]=[],this.binLengths[u]=0}f.exports=n;var o=e("./Broadphase"),d=e("../math/Vec3"),i=e("../shapes/Shape");n.prototype=new o,n.prototype.constructor=n;{var t=new d;new d}n.prototype.collisionPairs=function(e,f,n){function o(e,f,n,o,d,i,t){var l=(e-g)*v|0,u=(f-x)*A|0,p=(n-j)*C|0,b=I((o-g)*v),m=I((d-x)*A),N=I((i-j)*C);0>l?l=0:l>=s&&(l=s-1),0>u?u=0:u>=y&&(u=y-1),0>p?p=0:p>=c&&(p=c-1),0>b?b=0:b>=s&&(b=s-1),0>m?m=0:m>=y&&(m=y-1),0>N?N=0:N>=c&&(N=c-1),l*=a,u*=r,p*=w,b*=a,m*=r,N*=w;for(var O=l;b>=O;O+=a)for(var h=u;m>=h;h+=r)for(var k=p;N>=k;k+=w){var q=O+h+k;E[q][F[q]++]=t}}for(var d=e.numObjects(),l=e.bodies,u=this.aabbMax,p=this.aabbMin,s=this.nx,y=this.ny,c=this.nz,a=y*c,r=c,w=1,b=u.x,m=u.y,N=u.z,g=p.x,x=p.y,j=p.z,v=s/(b-g),A=y/(m-x),C=c/(N-j),O=(b-g)/s,h=(m-x)/y,k=(N-j)/c,q=.5*Math.sqrt(O*O+h*h+k*k),z=i.types,B=z.SPHERE,D=z.PLANE,E=(z.BOX,z.COMPOUND,z.CONVEXPOLYHEDRON,this.bins),F=this.binLengths,G=this.bins.length,H=0;H!==G;H++)F[H]=0;for(var I=Math.ceil,p=Math.min,u=Math.max,H=0;H!==d;H++){var J=l[H],K=J.shape;switch(K.type){case B:var L=J.position.x,M=J.position.y,P=J.position.z,Q=K.radius;o(L-Q,M-Q,P-Q,L+Q,M+Q,P+Q,J);break;case D:K.worldNormalNeedsUpdate&&K.computeWorldNormal(J.quaternion);var R=K.worldNormal,S=g+.5*O-J.position.x,T=x+.5*h-J.position.y,U=j+.5*k-J.position.z,V=t;V.set(S,T,U);for(var W=0,X=0;W!==s;W++,X+=a,V.y=T,V.x+=O)for(var Y=0,Z=0;Y!==y;Y++,Z+=r,V.z=U,V.y+=h)for(var $=0,_=0;$!==c;$++,_+=w,V.z+=k)if(V.dot(R)1)for(var nf=E[H],W=0;W!==ff;W++)for(var J=nf[W],Y=0;Y!==W;Y++){var of=nf[Y];this.needBroadphaseCollision(J,of)&&this.intersectionTest(J,of,f,n)}}this.makePairsUnique(f,n)}},{"../math/Vec3":30,"../shapes/Shape":43,"./Broadphase":5}],7:[function(e,f){function n(){o.apply(this)}f.exports=n;var o=e("./Broadphase"),d=e("./AABB");n.prototype=new o,n.prototype.constructor=n,n.prototype.collisionPairs=function(e,f,n){var o,d,i,t,l=e.bodies,u=l.length;for(o=0;o!==u;o++)for(d=0;d!==o;d++)i=l[o],t=l[d],this.needBroadphaseCollision(i,t)&&this.intersectionTest(i,t,f,n)};new d;n.prototype.aabbQuery=function(e,f,n){n=n||[];for(var o=0;oe){var n=f;f=e,e=n}return e+"-"+f in this.matrix},n.prototype.set=function(e,f,n){if(e=e.id,f=f.id,f>e){var o=f;f=e,e=o}n?this.matrix[e+"-"+f]=!0:delete this.matrix[e+"-"+f]},n.prototype.reset=function(){this.matrix={}},n.prototype.setNumObjects=function(){}},{}],9:[function(e,f){function n(e,f){this.from=e?e.clone():new i,this.to=f?f.clone():new i,this._direction=new i,this.precision=1e-4,this.checkCollisionResponse=!0,this.skipBackfaces=!1,this.collisionFilterMask=-1,this.collisionFilterGroup=-1,this.mode=n.ANY,this.result=new u,this.hasHit=!1,this.callback=function(){}}function o(e,f,n,o){o.vsub(f,G),n.vsub(f,a),e.vsub(f,r);var d,i,t=G.dot(G),l=G.dot(a),u=G.dot(r),p=a.dot(a),s=a.dot(r);return(d=p*u-l*s)>=0&&(i=t*s-l*u)>=0&&t*p-l*l>d+i}function d(e,f,n){n.vsub(e,G);var o=G.dot(f);f.mult(o,H),H.vadd(e,H);var d=n.distanceTo(H);return d}f.exports=n;var i=e("../math/Vec3"),t=e("../math/Quaternion"),l=e("../math/Transform"),u=(e("../shapes/ConvexPolyhedron"),e("../shapes/Box"),e("../collision/RaycastResult")),p=e("../shapes/Shape"),s=e("../collision/AABB");n.prototype.constructor=n,n.CLOSEST=1,n.ANY=2,n.ALL=4;var y=new s,c=[];n.prototype.intersectWorld=function(e,f){return this.mode=f.mode||n.ANY,this.result=f.result||new u,this.skipBackfaces=!!f.skipBackfaces,this.collisionFilterMask="undefined"!=typeof f.collisionFilterMask?f.collisionFilterMask:-1,this.collisionFilterGroup="undefined"!=typeof f.collisionFilterGroup?f.collisionFilterGroup:-1,f.from&&this.from.copy(f.from),f.to&&this.to.copy(f.to),this.callback=f.callback||function(){},this.hasHit=!1,this.result.reset(),this._updateDirection(),this.getAABB(y),c.length=0,e.broadphase.aabbQuery(e,y,c),this.intersectBodies(c),this.hasHit};var a=new i,r=new i;n.pointInTriangle=o;var w=new i,b=new t;n.prototype.intersectBody=function(e,f){f&&(this.result=f,this._updateDirection());var n=this.checkCollisionResponse;if((!n||e.collisionResponse)&&0!==(this.collisionFilterGroup&e.collisionFilterMask)&&0!==(e.collisionFilterGroup&this.collisionFilterMask))for(var o=w,d=b,i=0,t=e.shapes.length;t>i;i++){var l=e.shapes[i];if((!n||l.collisionResponse)&&(e.quaternion.mult(e.shapeOrientations[i],d),e.quaternion.vmult(e.shapeOffsets[i],o),o.vadd(e.position,o),this.intersectShape(l,d,o,e),this.result._shouldStop))break}},n.prototype.intersectBodies=function(e,f){f&&(this.result=f,this._updateDirection());for(var n=0,o=e.length;!this.result._shouldStop&&o>n;n++)this.intersectBody(e[n])},n.prototype._updateDirection=function(){this.to.vsub(this.from,this._direction),this._direction.normalize()},n.prototype.intersectShape=function(e,f,n,o){var i=this.from,t=d(i,this._direction,n);if(!(t>e.boundingSphereRadius)){var l=this[e.type];l&&l.call(this,e,f,n,o)}};{var m=(new i,new i,new i),N=new i,g=new i,x=new i;new i,new u}n.prototype.intersectBox=function(e,f,n,o){return this.intersectConvex(e.convexPolyhedronRepresentation,f,n,o)},n.prototype[p.types.BOX]=n.prototype.intersectBox,n.prototype.intersectPlane=function(e,f,n,o){var d=this.from,t=this.to,l=this._direction,u=new i(0,0,1);f.vmult(u,u);var p=new i;d.vsub(n,p);var s=p.dot(u);t.vsub(n,p);var y=p.dot(u);if(!(s*y>0||d.distanceTo(t)c)&&(c=p[0]),(null===y||p[1]a)&&(a=p[1])),null!==s){var w=[];e.getRectMinMax(s,y,c,a,w);for(var b=(w[0],w[1],s);c>=b;b++)for(var m=y;a>=m;m++){if(this.result._shouldStop)return;if(e.getConvexTrianglePillar(b,m,!1),l.pointToWorldFrame(o,f,e.pillarOffset,t),this.intersectConvex(e.pillarConvex,f,t,d,j),this.result._shouldStop)return;e.getConvexTrianglePillar(b,m,!0),l.pointToWorldFrame(o,f,e.pillarOffset,t),this.intersectConvex(e.pillarConvex,f,t,d,j)}}},n.prototype[p.types.HEIGHTFIELD]=n.prototype.intersectHeightfield;var v=new i,A=new i;n.prototype.intersectSphere=function(e,f,n,o){var d=this.from,i=this.to,t=e.radius,l=Math.pow(i.x-d.x,2)+Math.pow(i.y-d.y,2)+Math.pow(i.z-d.z,2),u=2*((i.x-d.x)*(d.x-n.x)+(i.y-d.y)*(d.y-n.y)+(i.z-d.z)*(d.z-n.z)),p=Math.pow(d.x-n.x,2)+Math.pow(d.y-n.y,2)+Math.pow(d.z-n.z,2)-Math.pow(t,2),s=Math.pow(u,2)-4*l*p,y=v,c=A;if(!(0>s))if(0===s)d.lerp(i,s,y),y.vsub(n,c),c.normalize(),this.reportIntersection(c,y,e,o,-1);else{var a=(-u-Math.sqrt(s))/(2*l),r=(-u+Math.sqrt(s))/(2*l);if(a>=0&&1>=a&&(d.lerp(i,a,y),y.vsub(n,c),c.normalize(),this.reportIntersection(c,y,e,o,-1)),this.result._shouldStop)return;r>=0&&1>=r&&(d.lerp(i,r,y),y.vsub(n,c),c.normalize(),this.reportIntersection(c,y,e,o,-1))}},n.prototype[p.types.SPHERE]=n.prototype.intersectSphere;var C=new i,O=(new i,new i,new i);n.prototype.intersectConvex=function(e,f,n,d,i){for(var t=C,l=O,u=i&&i.faceList||null,p=e.faces,s=e.vertices,y=e.faceNormals,c=this._direction,a=this.from,r=this.to,w=a.distanceTo(r),b=u?u.length:p.length,j=this.result,v=0;!j._shouldStop&&b>v;v++){var A=u?u[v]:v,h=p[A],k=y[A],q=f,z=n;l.copy(s[h[0]]),q.vmult(l,l),l.vadd(z,l),l.vsub(a,l),q.vmult(k,t);var B=c.dot(t);if(!(Math.abs(B)D)){c.mult(D,m),m.vadd(a,m),N.copy(s[h[0]]),q.vmult(N,N),z.vadd(N,N);for(var E=1;!j._shouldStop&&Ew||this.reportIntersection(t,m,e,d,A)}}}}},n.prototype[p.types.CONVEXPOLYHEDRON]=n.prototype.intersectConvex;var h=new i,k=new i,q=new i,z=new i,B=new i,D=new i,E=(new s,[]),F=new l;n.prototype.intersectTrimesh=function(e,f,n,d,i){var t=h,u=E,p=F,s=O,y=k,c=q,a=z,r=D,w=B,b=(i&&i.faceList||null,e.indices),j=(e.vertices,e.faceNormals,this.from),v=this.to,A=this._direction;p.position.copy(n),p.quaternion.copy(f),l.vectorToLocalFrame(n,f,A,y),l.pointToLocalFrame(n,f,j,c),l.pointToLocalFrame(n,f,v,a);var C=c.distanceSquared(a);e.tree.rayQuery(this,p,u);for(var G=0,H=u.length;!this.result._shouldStop&&G!==H;G++){var I=u[G];e.getNormal(I,t),e.getVertex(b[3*I],N),N.vsub(c,s);var J=y.dot(t),K=t.dot(s)/J;if(!(0>K)){y.scale(K,m),m.vadd(c,m),e.getVertex(b[3*I+1],g),e.getVertex(b[3*I+2],x);var L=m.distanceSquared(c);!o(m,g,N,x)&&!o(m,N,g,x)||L>C||(l.vectorToWorldFrame(f,t,w),l.pointToWorldFrame(n,f,m,r),this.reportIntersection(w,r,e,d,I))}}u.length=0},n.prototype[p.types.TRIMESH]=n.prototype.intersectTrimesh,n.prototype.reportIntersection=function(e,f,o,d,i){var t=this.from,l=this.to,u=t.distanceTo(f),p=this.result;if(!(this.skipBackfaces&&e.dot(this._direction)>0))switch(p.hitFaceIndex="undefined"!=typeof i?i:-1,this.mode){case n.ALL:this.hasHit=!0,p.set(t,l,e,f,o,d,u),p.hasHit=!0,this.callback(p);break;case n.CLOSEST:(uf;f++){for(var o=e[f],d=f-1;d>=0&&!(e[d].aabb.lowerBound.x<=o.aabb.lowerBound.x);d--)e[d+1]=e[d];e[d+1]=o}return e},n.insertionSortY=function(e){for(var f=1,n=e.length;n>f;f++){for(var o=e[f],d=f-1;d>=0&&!(e[d].aabb.lowerBound.y<=o.aabb.lowerBound.y);d--)e[d+1]=e[d];e[d+1]=o}return e},n.insertionSortZ=function(e){for(var f=1,n=e.length;n>f;f++){for(var o=e[f],d=f-1;d>=0&&!(e[d].aabb.lowerBound.z<=o.aabb.lowerBound.z);d--)e[d+1]=e[d];e[d+1]=o}return e},n.prototype.collisionPairs=function(e,f,o){var d,i,t=this.axisList,l=t.length,u=this.axisIndex;for(this.dirty&&(this.sortList(),this.dirty=!1),d=0;d!==l;d++){var p=t[d];for(i=d+1;l>i;i++){var s=t[i];if(this.needBroadphaseCollision(p,s)){if(!n.checkBounds(p,s,u))break;this.intersectionTest(p,s,f,o)}}}},n.prototype.sortList=function(){for(var e=this.axisList,f=this.axisIndex,o=e.length,d=0;d!==o;d++){var i=e[d];i.aabbNeedsUpdate&&i.computeAABB()}0===f?n.insertionSortX(e):1===f?n.insertionSortY(e):2===f&&n.insertionSortZ(e)},n.checkBounds=function(e,f,n){var o,d;0===n?(o=e.position.x,d=f.position.x):1===n?(o=e.position.y,d=f.position.y):2===n&&(o=e.position.z,d=f.position.z);var i=e.boundingRadius,t=f.boundingRadius,l=o+i,u=d-t;return l>u},n.prototype.autoDetectAxis=function(){for(var e=0,f=0,n=0,o=0,d=0,i=0,t=this.axisList,l=t.length,u=1/l,p=0;p!==l;p++){var s=t[p],y=s.position.x;e+=y,f+=y*y;var c=s.position.y;n+=c,o+=c*c;var a=s.position.z;d+=a,i+=a*a}var r=f-e*e*u,w=o-n*n*u,b=i-d*d*u;this.axisIndex=r>w?r>b?0:2:w>b?1:2},n.prototype.aabbQuery=function(e,f,n){n=n||[],this.dirty&&(this.sortList(),this.dirty=!1);var o=this.axisIndex,d="x";1===o&&(d="y"),2===o&&(d="z");for(var i=this.axisList,t=(f.lowerBound[d],f.upperBound[d],0);td;d++)for(var i=0;3>i;i++){for(var t=0,l=0;3>l;l++)t+=e.elements[d+3*l]*this.elements[l+3*i];o.elements[d+3*i]=t}return o},n.prototype.scale=function(e,f){f=f||new n;for(var o=this.elements,d=f.elements,i=0;3!==i;i++)d[3*i+0]=e.x*o[3*i+0],d[3*i+1]=e.y*o[3*i+1],d[3*i+2]=e.z*o[3*i+2];return f},n.prototype.solve=function(e,f){f=f||new o;for(var n=3,d=4,i=[],t=0;n*d>t;t++)i.push(0);var t,l;for(t=0;3>t;t++)for(l=0;3>l;l++)i[t+d*l]=this.elements[t+3*l];i[3]=e.x,i[7]=e.y,i[11]=e.z;var u,p,s=3,y=s,c=4;do{if(t=y-s,0===i[t+d*t])for(l=t+1;y>l;l++)if(0!==i[t+d*l]){u=c;do p=c-u,i[p+d*t]+=i[p+d*l];while(--u);break}if(0!==i[t+d*t])for(l=t+1;y>l;l++){var a=i[t+d*l]/i[t+d*t];u=c;do p=c-u,i[p+d*l]=t>=p?0:i[p+d*l]-i[p+d*t]*a;while(--u)}}while(--s);if(f.z=i[2*d+3]/i[2*d+2],f.y=(i[1*d+3]-i[1*d+2]*f.z)/i[1*d+1],f.x=(i[0*d+3]-i[0*d+2]*f.z-i[0*d+1]*f.y)/i[0*d+0],isNaN(f.x)||isNaN(f.y)||isNaN(f.z)||1/0===f.x||1/0===f.y||1/0===f.z)throw"Could not solve equation! Got x=["+f.toString()+"], b=["+e.toString()+"], A=["+this.toString()+"]";return f},n.prototype.e=function(e,f,n){return void 0===n?this.elements[f+3*e]:void(this.elements[f+3*e]=n)},n.prototype.copy=function(e){for(var f=0;fn;n++)e+=this.elements[n]+f;return e},n.prototype.reverse=function(e){e=e||new n;for(var f=3,o=6,d=[],i=0;f*o>i;i++)d.push(0);var i,t;for(i=0;3>i;i++)for(t=0;3>t;t++)d[i+o*t]=this.elements[i+3*t];d[3]=1,d[9]=0,d[15]=0,d[4]=0,d[10]=1,d[16]=0,d[5]=0,d[11]=0,d[17]=1;var l,u,p=3,s=p,y=o;do{if(i=s-p,0===d[i+o*i])for(t=i+1;s>t;t++)if(0!==d[i+o*t]){l=y;do u=y-l,d[u+o*i]+=d[u+o*t];while(--l);break}if(0!==d[i+o*i])for(t=i+1;s>t;t++){var c=d[i+o*t]/d[i+o*i];l=y;do u=y-l,d[u+o*t]=i>=u?0:d[u+o*t]-d[u+o*i]*c;while(--l)}}while(--p);i=2;do{t=i-1;do{var c=d[i+o*t]/d[i+o*i];l=o;do u=o-l,d[u+o*t]=d[u+o*t]-d[u+o*i]*c;while(--l)}while(t--)}while(--i);i=2;do{var c=1/d[i+o*i];l=o;do u=o-l,d[u+o*i]=d[u+o*i]*c;while(--l)}while(i--);i=2;do{t=2;do{if(u=d[f+t+o*i],isNaN(u)||1/0===u)throw"Could not reverse! A=["+this.toString()+"]";e.e(i,t,u)}while(t--)}while(i--);return e},n.prototype.setRotationFromQuaternion=function(e){var f=e.x,n=e.y,o=e.z,d=e.w,i=f+f,t=n+n,l=o+o,u=f*i,p=f*t,s=f*l,y=n*t,c=n*l,a=o*l,r=d*i,w=d*t,b=d*l,m=this.elements;return m[0]=1-(y+a),m[1]=p-b,m[2]=s+w,m[3]=p+b,m[4]=1-(u+a),m[5]=c-r,m[6]=s-w,m[7]=c+r,m[8]=1-(u+y),this},n.prototype.transpose=function(e){e=e||new n;for(var f=e.elements,o=this.elements,d=0;3!==d;d++)for(var i=0;3!==i;i++)f[3*d+i]=o[3*i+d];return e}},{"./Vec3":30}],28:[function(e,f){function n(e,f,n,o){this.x=void 0!==e?e:0,this.y=void 0!==f?f:0,this.z=void 0!==n?n:0,this.w=void 0!==o?o:1}f.exports=n;var o=e("./Vec3");n.prototype.set=function(e,f,n,o){this.x=e,this.y=f,this.z=n,this.w=o},n.prototype.toString=function(){return this.x+","+this.y+","+this.z+","+this.w},n.prototype.toArray=function(){return[this.x,this.y,this.z,this.w]},n.prototype.setFromAxisAngle=function(e,f){var n=Math.sin(.5*f);this.x=e.x*n,this.y=e.y*n,this.z=e.z*n,this.w=Math.cos(.5*f)},n.prototype.toAxisAngle=function(e){e=e||new o,this.normalize();var f=2*Math.acos(this.w),n=Math.sqrt(1-this.w*this.w);return.001>n?(e.x=this.x,e.y=this.y,e.z=this.z):(e.x=this.x/n,e.y=this.y/n,e.z=this.z/n),[e,f]};var d=new o,i=new o;n.prototype.setFromVectors=function(e,f){if(e.isAntiparallelTo(f)){var n=d,o=i;e.tangents(n,o),this.setFromAxisAngle(n,Math.PI)}else{var t=e.cross(f);this.x=t.x,this.y=t.y,this.z=t.z,this.w=Math.sqrt(Math.pow(e.norm(),2)*Math.pow(f.norm(),2))+e.dot(f),this.normalize()}};var t=new o,l=new o,u=new o;n.prototype.mult=function(e,f){f=f||new n;var o=this.w,d=t,i=l,p=u;return d.set(this.x,this.y,this.z),i.set(e.x,e.y,e.z),f.w=o*e.w-d.dot(i),d.cross(i,p),f.x=o*i.x+e.w*d.x+p.x,f.y=o*i.y+e.w*d.y+p.y,f.z=o*i.z+e.w*d.z+p.z,f},n.prototype.inverse=function(e){var f=this.x,o=this.y,d=this.z,i=this.w;e=e||new n,this.conjugate(e);var t=1/(f*f+o*o+d*d+i*i);return e.x*=t,e.y*=t,e.z*=t,e.w*=t,e},n.prototype.conjugate=function(e){return e=e||new n,e.x=-this.x,e.y=-this.y,e.z=-this.z,e.w=this.w,e},n.prototype.normalize=function(){var e=Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w);0===e?(this.x=0,this.y=0,this.z=0,this.w=0):(e=1/e,this.x*=e,this.y*=e,this.z*=e,this.w*=e)},n.prototype.normalizeFast=function(){var e=(3-(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w))/2;0===e?(this.x=0,this.y=0,this.z=0,this.w=0):(this.x*=e,this.y*=e,this.z*=e,this.w*=e)},n.prototype.vmult=function(e,f){f=f||new o;var n=e.x,d=e.y,i=e.z,t=this.x,l=this.y,u=this.z,p=this.w,s=p*n+l*i-u*d,y=p*d+u*n-t*i,c=p*i+t*d-l*n,a=-t*n-l*d-u*i;return f.x=s*p+a*-t+y*-u-c*-l,f.y=y*p+a*-l+c*-t-s*-u,f.z=c*p+a*-u+s*-l-y*-t,f},n.prototype.copy=function(e){return this.x=e.x,this.y=e.y,this.z=e.z,this.w=e.w,this},n.prototype.toEuler=function(e,f){f=f||"YZX";var n,o,d,i=this.x,t=this.y,l=this.z,u=this.w;switch(f){case"YZX":var p=i*t+l*u;if(p>.499&&(n=2*Math.atan2(i,u),o=Math.PI/2,d=0),-.499>p&&(n=-2*Math.atan2(i,u),o=-Math.PI/2,d=0),isNaN(n)){var s=i*i,y=t*t,c=l*l;n=Math.atan2(2*t*u-2*i*l,1-2*y-2*c),o=Math.asin(2*p),d=Math.atan2(2*i*u-2*t*l,1-2*s-2*c)}break;default:throw new Error("Euler order "+f+" not supported yet.")}e.y=n,e.z=o,e.x=d},n.prototype.setFromEuler=function(e,f,n,o){o=o||"XYZ";var d=Math.cos(e/2),i=Math.cos(f/2),t=Math.cos(n/2),l=Math.sin(e/2),u=Math.sin(f/2),p=Math.sin(n/2);return"XYZ"===o?(this.x=l*i*t+d*u*p,this.y=d*u*t-l*i*p,this.z=d*i*p+l*u*t,this.w=d*i*t-l*u*p):"YXZ"===o?(this.x=l*i*t+d*u*p,this.y=d*u*t-l*i*p,this.z=d*i*p-l*u*t,this.w=d*i*t+l*u*p):"ZXY"===o?(this.x=l*i*t-d*u*p,this.y=d*u*t+l*i*p,this.z=d*i*p+l*u*t,this.w=d*i*t-l*u*p):"ZYX"===o?(this.x=l*i*t-d*u*p,this.y=d*u*t+l*i*p,this.z=d*i*p-l*u*t,this.w=d*i*t+l*u*p):"YZX"===o?(this.x=l*i*t+d*u*p,this.y=d*u*t+l*i*p,this.z=d*i*p-l*u*t,this.w=d*i*t-l*u*p):"XZY"===o&&(this.x=l*i*t-d*u*p,this.y=d*u*t-l*i*p,this.z=d*i*p+l*u*t,this.w=d*i*t+l*u*p),this},n.prototype.clone=function(){return new n(this.x,this.y,this.z,this.w)}},{"./Vec3":30}],29:[function(e,f){function n(e){e=e||{},this.position=new o,e.position&&this.position.copy(e.position),this.quaternion=new d,e.quaternion&&this.quaternion.copy(e.quaternion)}var o=e("./Vec3"),d=e("./Quaternion");f.exports=n;var i=new d;n.pointToLocalFrame=function(e,f,n,d){var d=d||new o;return n.vsub(e,d),f.conjugate(i),i.vmult(d,d),d},n.prototype.pointToLocal=function(e,f){return n.pointToLocalFrame(this.position,this.quaternion,e,f)},n.pointToWorldFrame=function(e,f,n,d){var d=d||new o;return f.vmult(n,d),d.vadd(e,d),d},n.prototype.pointToWorld=function(e,f){return n.pointToWorldFrame(this.position,this.quaternion,e,f)},n.prototype.vectorToWorldFrame=function(e,f){var f=f||new o;return this.quaternion.vmult(e,f),f},n.vectorToWorldFrame=function(e,f,n){return e.vmult(f,n),n},n.vectorToLocalFrame=function(e,f,n,d){var d=d||new o;return f.w*=-1,f.vmult(n,d),f.w*=-1,d}},{"./Quaternion":28,"./Vec3":30}],30:[function(e,f){function n(e,f,n){this.x=e||0,this.y=f||0,this.z=n||0}f.exports=n;var o=e("./Mat3");n.ZERO=new n(0,0,0),n.UNIT_X=new n(1,0,0),n.UNIT_Y=new n(0,1,0),n.UNIT_Z=new n(0,0,1),n.prototype.cross=function(e,f){var o=e.x,d=e.y,i=e.z,t=this.x,l=this.y,u=this.z;return f=f||new n,f.x=l*i-u*d,f.y=u*o-t*i,f.z=t*d-l*o,f},n.prototype.set=function(e,f,n){return this.x=e,this.y=f,this.z=n,this},n.prototype.setZero=function(){this.x=this.y=this.z=0},n.prototype.vadd=function(e,f){return f?(f.x=e.x+this.x,f.y=e.y+this.y,f.z=e.z+this.z,void 0):new n(this.x+e.x,this.y+e.y,this.z+e.z)},n.prototype.vsub=function(e,f){return f?(f.x=this.x-e.x,f.y=this.y-e.y,f.z=this.z-e.z,void 0):new n(this.x-e.x,this.y-e.y,this.z-e.z)},n.prototype.crossmat=function(){return new o([0,-this.z,this.y,this.z,0,-this.x,-this.y,this.x,0])},n.prototype.normalize=function(){var e=this.x,f=this.y,n=this.z,o=Math.sqrt(e*e+f*f+n*n);if(o>0){var d=1/o;this.x*=d,this.y*=d,this.z*=d}else this.x=0,this.y=0,this.z=0;return o},n.prototype.unit=function(e){e=e||new n;var f=this.x,o=this.y,d=this.z,i=Math.sqrt(f*f+o*o+d*d);return i>0?(i=1/i,e.x=f*i,e.y=o*i,e.z=d*i):(e.x=1,e.y=0,e.z=0),e},n.prototype.norm=function(){var e=this.x,f=this.y,n=this.z;return Math.sqrt(e*e+f*f+n*n)},n.prototype.length=n.prototype.norm,n.prototype.norm2=function(){return this.dot(this)},n.prototype.lengthSquared=n.prototype.norm2,n.prototype.distanceTo=function(e){var f=this.x,n=this.y,o=this.z,d=e.x,i=e.y,t=e.z;return Math.sqrt((d-f)*(d-f)+(i-n)*(i-n)+(t-o)*(t-o))},n.prototype.distanceSquared=function(e){var f=this.x,n=this.y,o=this.z,d=e.x,i=e.y,t=e.z;return(d-f)*(d-f)+(i-n)*(i-n)+(t-o)*(t-o)},n.prototype.mult=function(e,f){f=f||new n;var o=this.x,d=this.y,i=this.z;return f.x=e*o,f.y=e*d,f.z=e*i,f},n.prototype.scale=n.prototype.mult,n.prototype.dot=function(e){return this.x*e.x+this.y*e.y+this.z*e.z},n.prototype.isZero=function(){return 0===this.x&&0===this.y&&0===this.z},n.prototype.negate=function(e){return e=e||new n,e.x=-this.x,e.y=-this.y,e.z=-this.z,e};var d=new n,i=new n;n.prototype.tangents=function(e,f){var n=this.norm();if(n>0){var o=d,t=1/n;o.set(this.x*t,this.y*t,this.z*t);var l=i;Math.abs(o.x)<.9?(l.set(1,0,0),o.cross(l,e)):(l.set(0,1,0),o.cross(l,e)),o.cross(e,f)}else e.set(1,0,0),f.set(0,1,0)},n.prototype.toString=function(){return this.x+","+this.y+","+this.z},n.prototype.toArray=function(){return[this.x,this.y,this.z]},n.prototype.copy=function(e){return this.x=e.x,this.y=e.y,this.z=e.z,this},n.prototype.lerp=function(e,f,n){var o=this.x,d=this.y,i=this.z;n.x=o+(e.x-o)*f,n.y=d+(e.y-d)*f,n.z=i+(e.z-i)*f},n.prototype.almostEquals=function(e,f){return void 0===f&&(f=1e-6),Math.abs(this.x-e.x)>f||Math.abs(this.y-e.y)>f||Math.abs(this.z-e.z)>f?!1:!0},n.prototype.almostZero=function(e){return void 0===e&&(e=1e-6),Math.abs(this.x)>e||Math.abs(this.y)>e||Math.abs(this.z)>e?!1:!0};var t=new n;n.prototype.isAntiparallelTo=function(e,f){return this.negate(t),t.almostEquals(e,f)},n.prototype.clone=function(){return new n(this.x,this.y,this.z)}},{"./Mat3":27}],31:[function(e,f){function n(e){e=e||{},o.apply(this),this.id=n.idCounter++,this.world=null,this.preStep=null,this.postStep=null,this.vlambda=new d,this.collisionFilterGroup="number"==typeof e.collisionFilterGroup?e.collisionFilterGroup:1,this.collisionFilterMask="number"==typeof e.collisionFilterMask?e.collisionFilterMask:1,this.collisionResponse=!0,this.position=new d,e.position&&this.position.copy(e.position),this.previousPosition=new d,this.initPosition=new d,this.velocity=new d,e.velocity&&this.velocity.copy(e.velocity),this.initVelocity=new d,this.force=new d;var f="number"==typeof e.mass?e.mass:0;this.mass=f,this.invMass=f>0?1/f:0,this.material=e.material||null,this.linearDamping="number"==typeof e.linearDamping?e.linearDamping:.01,this.type=0>=f?n.STATIC:n.DYNAMIC,typeof e.type==typeof n.STATIC&&(this.type=e.type),this.allowSleep="undefined"!=typeof e.allowSleep?e.allowSleep:!0,this.sleepState=0,this.sleepSpeedLimit="undefined"!=typeof e.sleepSpeedLimit?e.sleepSpeedLimit:.1,this.sleepTimeLimit="undefined"!=typeof e.sleepTimeLimit?e.sleepTimeLimit:1,this.timeLastSleepy=0,this._wakeUpAfterNarrowphase=!1,this.torque=new d,this.quaternion=new t,e.quaternion&&this.quaternion.copy(e.quaternion),this.initQuaternion=new t,this.angularVelocity=new d,e.angularVelocity&&this.angularVelocity.copy(e.angularVelocity),this.initAngularVelocity=new d,this.interpolatedPosition=new d,this.interpolatedQuaternion=new t,this.shapes=[],this.shapeOffsets=[],this.shapeOrientations=[],this.inertia=new d,this.invInertia=new d,this.invInertiaWorld=new i,this.invMassSolve=0,this.invInertiaSolve=new d,this.invInertiaWorldSolve=new i,this.fixedRotation="undefined"!=typeof e.fixedRotation?e.fixedRotation:!1,this.angularDamping="undefined"!=typeof e.angularDamping?e.angularDamping:.01,this.aabb=new l,this.aabbNeedsUpdate=!0,this.wlambda=new d,e.shape&&this.addShape(e.shape),this.updateMassProperties()}f.exports=n;var o=e("../utils/EventTarget"),d=(e("../shapes/Shape"),e("../math/Vec3")),i=e("../math/Mat3"),t=e("../math/Quaternion"),l=(e("../material/Material"),e("../collision/AABB")),u=e("../shapes/Box");n.prototype=new o,n.prototype.constructor=n,n.DYNAMIC=1,n.STATIC=2,n.KINEMATIC=4,n.AWAKE=0,n.SLEEPY=1,n.SLEEPING=2,n.idCounter=0,n.prototype.wakeUp=function(){var e=this.sleepState;this.sleepState=0,e===n.SLEEPING&&this.dispatchEvent({type:"wakeup"})},n.prototype.sleep=function(){this.sleepState=n.SLEEPING,this.velocity.set(0,0,0),this.angularVelocity.set(0,0,0)},n.sleepyEvent={type:"sleepy"},n.sleepEvent={type:"sleep"},n.prototype.sleepTick=function(e){if(this.allowSleep){var f=this.sleepState,o=this.velocity.norm2()+this.angularVelocity.norm2(),d=Math.pow(this.sleepSpeedLimit,2);f===n.AWAKE&&d>o?(this.sleepState=n.SLEEPY,this.timeLastSleepy=e,this.dispatchEvent(n.sleepyEvent)):f===n.SLEEPY&&o>d?this.wakeUp():f===n.SLEEPY&&e-this.timeLastSleepy>this.sleepTimeLimit&&(this.sleep(),this.dispatchEvent(n.sleepEvent))}},n.prototype.updateSolveMassProperties=function(){this.sleepState===n.SLEEPING||this.type===n.KINEMATIC?(this.invMassSolve=0,this.invInertiaSolve.setZero(),this.invInertiaWorldSolve.setZero()):(this.invMassSolve=this.invMass,this.invInertiaSolve.copy(this.invInertia),this.invInertiaWorldSolve.copy(this.invInertiaWorld))},n.prototype.pointToLocalFrame=function(e,f){var f=f||new d;return e.vsub(this.position,f),this.quaternion.conjugate().vmult(f,f),f},n.prototype.vectorToLocalFrame=function(e,f){var f=f||new d;return this.quaternion.conjugate().vmult(e,f),f},n.prototype.pointToWorldFrame=function(e,f){var f=f||new d;return this.quaternion.vmult(e,f),f.vadd(this.position,f),f},n.prototype.vectorToWorldFrame=function(e,f){var f=f||new d;return this.quaternion.vmult(e,f),f};var p=new d,s=new t;n.prototype.addShape=function(e,f,n){var o=new d,i=new t;return f&&o.copy(f),n&&i.copy(n),this.shapes.push(e),this.shapeOffsets.push(o),this.shapeOrientations.push(i),this.updateMassProperties(),this.updateBoundingRadius(),this.aabbNeedsUpdate=!0,this},n.prototype.updateBoundingRadius=function(){for(var e=this.shapes,f=this.shapeOffsets,n=e.length,o=0,d=0;d!==n;d++){var i=e[d];i.updateBoundingSphereRadius();var t=f[d].norm(),l=i.boundingSphereRadius;t+l>o&&(o=t+l)}this.boundingRadius=o};var y=new l;n.prototype.computeAABB=function(){for(var e=this.shapes,f=this.shapeOffsets,n=this.shapeOrientations,o=e.length,d=p,i=s,t=this.quaternion,l=this.aabb,u=y,c=0;c!==o;c++){var a=e[c];n[c].mult(t,i),i.vmult(f[c],d),d.vadd(this.position,d),a.calculateWorldAABB(d,i,u.lowerBound,u.upperBound),0===c?l.copy(u):l.extend(u)}this.aabbNeedsUpdate=!1};{var c=new i,a=new i;new i}n.prototype.updateInertiaWorld=function(e){var f=this.invInertia;if(f.x!==f.y||f.y!==f.z||e){var n=c,o=a;n.setRotationFromQuaternion(this.quaternion),n.transpose(o),n.scale(f,n),n.mmult(o,this.invInertiaWorld)}else;};var r=new d,w=new d;n.prototype.applyForce=function(e,f){if(this.type===n.DYNAMIC){var o=r;f.vsub(this.position,o);var d=w;o.cross(e,d),this.force.vadd(e,this.force),this.torque.vadd(d,this.torque)}};var b=new d,m=new d;n.prototype.applyLocalForce=function(e,f){if(this.type===n.DYNAMIC){var o=b,d=m;this.vectorToWorldFrame(e,o),this.pointToWorldFrame(f,d),this.applyForce(o,d)}};var N=new d,g=new d,x=new d;n.prototype.applyImpulse=function(e,f){if(this.type===n.DYNAMIC){var o=N;f.vsub(this.position,o);var d=g;d.copy(e),d.mult(this.invMass,d),this.velocity.vadd(d,this.velocity);var i=x;o.cross(e,i),this.invInertiaWorld.vmult(i,i),this.angularVelocity.vadd(i,this.angularVelocity)}};var j=new d,v=new d;n.prototype.applyLocalImpulse=function(e,f){if(this.type===n.DYNAMIC){var o=j,d=v;this.vectorToWorldFrame(e,o),this.pointToWorldFrame(f,d),this.applyImpulse(o,d)}};var A=new d;n.prototype.updateMassProperties=function(){var e=A;this.invMass=this.mass>0?1/this.mass:0;var f=this.inertia,n=this.fixedRotation;this.computeAABB(),e.set((this.aabb.upperBound.x-this.aabb.lowerBound.x)/2,(this.aabb.upperBound.y-this.aabb.lowerBound.y)/2,(this.aabb.upperBound.z-this.aabb.lowerBound.z)/2),u.calculateInertia(e,this.mass,f),this.invInertia.set(f.x>0&&!n?1/f.x:0,f.y>0&&!n?1/f.y:0,f.z>0&&!n?1/f.z:0),this.updateInertiaWorld(!0)},n.prototype.getVelocityAtWorldPoint=function(e,f){var n=new d;return e.vsub(this.position,n),this.angularVelocity.cross(n,f),this.velocity.vadd(f,f),f}},{"../collision/AABB":3,"../material/Material":25,"../math/Mat3":27,"../math/Quaternion":28,"../math/Vec3":30,"../shapes/Box":37,"../shapes/Shape":43,"../utils/EventTarget":49}],32:[function(e,f){function n(e){this.chassisBody=e.chassisBody,this.wheelInfos=[],this.sliding=!1,this.world=null,this.indexRightAxis="undefined"!=typeof e.indexRightAxis?e.indexRightAxis:1,this.indexForwardAxis="undefined"!=typeof e.indexForwardAxis?e.indexForwardAxis:0,this.indexUpAxis="undefined"!=typeof e.indexUpAxis?e.indexUpAxis:2}function o(e,f,n,o,i){var t=0,l=n,u=x,p=j,s=v;e.getVelocityAtWorldPoint(l,u),f.getVelocityAtWorldPoint(l,p),u.vsub(p,s);var y=o.dot(s),c=d(e,n,o),a=d(f,n,o),r=1,w=r/(c+a);return t=-y*w,t>i&&(t=i),-i>t&&(t=-i),t}function d(e,f,n){var o=A,d=C,i=O,t=h;return f.vsub(e.position,o),o.cross(n,d),e.invInertiaWorld.vmult(d,t),t.cross(o,i),e.invMass+n.dot(i)}function i(e,f,n,o,d,i){var t=d.norm2();if(t>1.1)return 0;var l=k,u=q,p=z;e.getVelocityAtWorldPoint(f,l),n.getVelocityAtWorldPoint(o,u),l.vsub(u,p);var s=d.dot(p),y=.2,c=1/(e.invMass+n.invMass),i=-y*s*c;return i}var t=(e("./Body"),e("../math/Vec3")),l=e("../math/Quaternion"),u=(e("../collision/RaycastResult"),e("../collision/Ray")),p=e("../objects/WheelInfo");f.exports=n;{var s=(new t,new t,new t,new t),y=new t,c=new t;new u}n.prototype.addWheel=function(e){e=e||{};var f=new p(e),n=this.wheelInfos.length;return this.wheelInfos.push(f),n},n.prototype.setSteeringValue=function(e,f){var n=this.wheelInfos[f];n.steering=e};new t;n.prototype.applyEngineForce=function(e,f){this.wheelInfos[f].engineForce=e},n.prototype.setBrake=function(e,f){this.wheelInfos[f].brake=e},n.prototype.addToWorld=function(e){this.constraints;e.add(this.chassisBody);var f=this;this.preStepCallback=function(){f.updateVehicle(e.dt)},e.addEventListener("preStep",this.preStepCallback),this.world=e},n.prototype.getVehicleAxisWorld=function(e,f){f.set(0===e?1:0,1===e?1:0,2===e?1:0),this.chassisBody.vectorToWorldFrame(f,f)},n.prototype.updateVehicle=function(e){for(var f=this.wheelInfos,n=f.length,o=this.chassisBody,d=0;n>d;d++)this.updateWheelTransform(d);this.currentVehicleSpeedKmHour=3.6*o.velocity.norm();var i=new t;this.getVehicleAxisWorld(this.indexForwardAxis,i),i.dot(o.velocity)<0&&(this.currentVehicleSpeedKmHour*=-1);for(var d=0;n>d;d++)this.castRay(f[d]);this.updateSuspension(e);for(var l=new t,u=new t,d=0;n>d;d++){var p=f[d],s=p.suspensionForce;s>p.maxSuspensionForce&&(s=p.maxSuspensionForce),p.raycastResult.hitNormalWorld.scale(s*e,l),p.raycastResult.hitPointWorld.vsub(o.position,u),o.applyImpulse(l,p.raycastResult.hitPointWorld)}this.updateFriction(e);var y=new t,c=new t,a=new t;for(d=0;n>d;d++){var p=f[d];o.getVelocityAtWorldPoint(p.chassisConnectionPointWorld,a);var r=1;switch(this.indexUpAxis){case 1:r=-1}if(p.isInContact){this.getVehicleAxisWorld(this.indexForwardAxis,c);var w=c.dot(p.raycastResult.hitNormalWorld);p.raycastResult.hitNormalWorld.scale(w,y),c.vsub(y,c);var b=c.dot(a);p.deltaRotation=r*b*e/p.radius}!p.sliding&&p.isInContact||0===p.engineForce||!p.useCustomSlidingRotationalSpeed||(p.deltaRotation=(p.engineForce>0?1:-1)*p.customSlidingRotationalSpeed*e),Math.abs(p.brake)>Math.abs(p.engineForce)&&(p.deltaRotation=0),p.rotation+=p.deltaRotation,p.deltaRotation*=.99}},n.prototype.updateSuspension=function(){for(var e=this.chassisBody,f=e.mass,n=this.wheelInfos,o=n.length,d=0;o>d;d++){var i=n[d];if(i.isInContact){var t,l=i.suspensionRestLength,u=i.suspensionLength,p=l-u;t=i.suspensionStiffness*p*i.clippedInvContactDotSuspension;var s,y=i.suspensionRelativeVelocity;s=0>y?i.dampingCompression:i.dampingRelaxation,t-=s*y,i.suspensionForce=t*f,i.suspensionForce<0&&(i.suspensionForce=0)}else i.suspensionForce=0}},n.prototype.removeFromWorld=function(e){this.constraints;e.remove(this.chassisBody),e.removeEventListener("preStep",this.preStepCallback),this.world=null};var a=new t,r=new t;n.prototype.castRay=function(e){var f=a,n=r;this.updateWheelTransformWorld(e);var o=this.chassisBody,d=-1,i=e.suspensionRestLength+e.radius;e.directionWorld.scale(i,f);var l=e.chassisConnectionPointWorld;l.vadd(f,n);var u=e.raycastResult;u.reset();var p=o.collisionResponse;o.collisionResponse=!1,this.world.rayTest(l,n,u),o.collisionResponse=p;var s=u.body;if(e.raycastResult.groundObject=0,s){d=u.distance,e.raycastResult.hitNormalWorld=u.hitNormalWorld,e.isInContact=!0;var y=u.distance;e.suspensionLength=y-e.radius;var c=e.suspensionRestLength-e.maxSuspensionTravel,w=e.suspensionRestLength+e.maxSuspensionTravel;e.suspensionLengthw&&(e.suspensionLength=w,e.raycastResult.reset());var b=e.raycastResult.hitNormalWorld.dot(e.directionWorld),m=new t;o.getVelocityAtWorldPoint(e.raycastResult.hitPointWorld,m);var N=e.raycastResult.hitNormalWorld.dot(m);if(b>=-.1)e.suspensionRelativeVelocity=0,e.clippedInvContactDotSuspension=10;else{var g=-1/b;e.suspensionRelativeVelocity=N*g,e.clippedInvContactDotSuspension=g}}else e.suspensionLength=e.suspensionRestLength+0*e.maxSuspensionTravel,e.suspensionRelativeVelocity=0,e.directionWorld.scale(-1,e.raycastResult.hitNormalWorld),e.clippedInvContactDotSuspension=1;return d},n.prototype.updateWheelTransformWorld=function(e){e.isInContact=!1;var f=this.chassisBody;f.pointToWorldFrame(e.chassisConnectionPointLocal,e.chassisConnectionPointWorld),f.vectorToWorldFrame(e.directionLocal,e.directionWorld),f.vectorToWorldFrame(e.axleLocal,e.axleWorld)},n.prototype.updateWheelTransform=function(e){var f=s,n=y,o=c,d=this.wheelInfos[e];this.updateWheelTransformWorld(d),d.directionLocal.scale(-1,f),n.copy(d.axleLocal),f.cross(n,o),o.normalize(),n.normalize();var i=d.steering,t=new l;t.setFromAxisAngle(f,i);var u=new l;u.setFromAxisAngle(n,d.rotation);var p=d.worldTransform.quaternion;this.chassisBody.quaternion.mult(t,p),p.mult(u,p),p.normalize();var a=d.worldTransform.position;a.copy(d.directionWorld),a.scale(d.suspensionLength,a),a.vadd(d.chassisConnectionPointWorld,a)};var w=[new t(1,0,0),new t(0,1,0),new t(0,0,1)];n.prototype.getWheelTransformWorld=function(e){return this.wheelInfos[e].worldTransform};var b=new t,m=[],N=[],g=1;n.prototype.updateFriction=function(e){for(var f=b,n=this.wheelInfos,d=n.length,l=this.chassisBody,u=N,p=m,s=0,y=0;d>y;y++){var c=n[y],a=c.raycastResult.body;a&&s++,c.sideImpulse=0,c.forwardImpulse=0,u[y]||(u[y]=new t),p[y]||(p[y]=new t)}for(var y=0;d>y;y++){var c=n[y],a=c.raycastResult.body;if(a){var r=p[y],x=this.getWheelTransformWorld(y);x.vectorToWorldFrame(w[this.indexRightAxis],r);var j=c.raycastResult.hitNormalWorld,v=r.dot(j);j.scale(v,f),r.vsub(f,r),r.normalize(),j.cross(r,u[y]),u[y].normalize(),c.sideImpulse=i(l,c.raycastResult.hitPointWorld,a,c.raycastResult.hitPointWorld,r),c.sideImpulse*=g}}var A=1,C=.5;this.sliding=!1;for(var y=0;d>y;y++){var c=n[y],a=c.raycastResult.body,O=0;if(c.slipInfo=1,a){var h=0,k=c.brake?c.brake:h;O=o(l,a,c.raycastResult.hitPointWorld,u[y],k),O+=c.engineForce*e;var q=k/O;c.slipInfo*=q}if(c.forwardImpulse=0,c.skidInfo=1,a){c.skidInfo=1;var z=c.suspensionForce*e*c.frictionSlip,B=z,D=z*B;c.forwardImpulse=O;var E=c.forwardImpulse*C,F=c.sideImpulse*A,G=E*E+F*F;if(c.sliding=!1,G>D){this.sliding=!0,c.sliding=!0;var q=z/Math.sqrt(G);c.skidInfo*=q}}}if(this.sliding)for(var y=0;d>y;y++){var c=n[y];0!==c.sideImpulse&&c.skidInfo<1&&(c.forwardImpulse*=c.skidInfo,c.sideImpulse*=c.skidInfo)}for(var y=0;d>y;y++){var c=n[y],H=new t;if(H.copy(c.raycastResult.hitPointWorld),0!==c.forwardImpulse){var I=new t;u[y].scale(c.forwardImpulse,I),l.applyImpulse(I,H)}if(0!==c.sideImpulse){var a=c.raycastResult.body,J=new t;J.copy(c.raycastResult.hitPointWorld);var K=new t;p[y].scale(c.sideImpulse,K),l.pointToLocalFrame(H,H),H["xyz"[this.indexUpAxis]]*=c.rollInfluence,l.pointToWorldFrame(H,H),l.applyImpulse(K,H),K.scale(-1,K),a.applyImpulse(K,J)}}};var x=new t,j=new t,v=new t,A=new t,C=new t,O=new t,h=new t,k=new t,q=new t,z=new t},{"../collision/Ray":9,"../collision/RaycastResult":10,"../math/Quaternion":28,"../math/Vec3":30,"../objects/WheelInfo":36,"./Body":31}],33:[function(e,f){function n(e){if(this.wheelBodies=[],this.coordinateSystem="undefined"==typeof e.coordinateSystem?new t(1,2,3):e.coordinateSystem.clone(),this.chassisBody=e.chassisBody,!this.chassisBody){var f=new i(new t(5,2,.5));this.chassisBody=new o(1,f)}this.constraints=[],this.wheelAxes=[],this.wheelForces=[]}var o=e("./Body"),d=e("../shapes/Sphere"),i=e("../shapes/Box"),t=e("../math/Vec3"),l=e("../constraints/HingeConstraint");f.exports=n,n.prototype.addWheel=function(e){e=e||{};var f=e.body;f||(f=new o(1,new d(1.2))),this.wheelBodies.push(f),this.wheelForces.push(0);var n=(new t,"undefined"!=typeof e.position?e.position.clone():new t),i=new t;this.chassisBody.pointToWorldFrame(n,i),f.position.set(i.x,i.y,i.z);var u="undefined"!=typeof e.axis?e.axis.clone():new t(0,1,0);this.wheelAxes.push(u);var p=new l(this.chassisBody,f,{pivotA:n,axisA:u,pivotB:t.ZERO,axisB:u,collideConnected:!1});return this.constraints.push(p),this.wheelBodies.length-1},n.prototype.setSteeringValue=function(e,f){var n=this.wheelAxes[f],o=Math.cos(e),d=Math.sin(e),i=n.x,t=n.y;this.constraints[f].axisA.set(o*i-d*t,d*i+o*t,0)},n.prototype.setMotorSpeed=function(e,f){var n=this.constraints[f];n.enableMotor(),n.motorTargetVelocity=e},n.prototype.disableMotor=function(e){var f=this.constraints[e]; +f.disableMotor()};var u=new t;n.prototype.setWheelForce=function(e,f){this.wheelForces[f]=e},n.prototype.applyWheelForce=function(e,f){var n=this.wheelAxes[f],o=this.wheelBodies[f],d=o.torque;n.scale(e,u),o.vectorToWorldFrame(u,u),d.vadd(u,d)},n.prototype.addToWorld=function(e){for(var f=this.constraints,n=this.wheelBodies.concat([this.chassisBody]),o=0;othis.particles.length&&this.neighbors.pop())};var d=new o;n.prototype.getNeighbors=function(e,f){for(var n=this.particles.length,o=e.id,i=this.smoothingRadius*this.smoothingRadius,t=d,l=0;l!==n;l++){var u=this.particles[l];u.position.vsub(e.position,t),o!==u.id&&t.norm2()=-.1)this.suspensionRelativeVelocity=0,this.clippedInvContactDotSuspension=10;else{var d=-1/n;this.suspensionRelativeVelocity=o*d,this.clippedInvContactDotSuspension=d}}else f.suspensionLength=this.suspensionRestLength,this.suspensionRelativeVelocity=0,f.directionWorld.scale(-1,f.hitNormalWorld),this.clippedInvContactDotSuspension=1}},{"../collision/RaycastResult":10,"../math/Transform":29,"../math/Vec3":30,"../utils/Utils":53}],37:[function(e,f){function n(e){o.call(this),this.type=o.types.BOX,this.halfExtents=e,this.convexPolyhedronRepresentation=null,this.updateConvexPolyhedronRepresentation(),this.updateBoundingSphereRadius()}f.exports=n;var o=e("./Shape"),d=e("../math/Vec3"),i=e("./ConvexPolyhedron");n.prototype=new o,n.prototype.constructor=n,n.prototype.updateConvexPolyhedronRepresentation=function(){var e=this.halfExtents.x,f=this.halfExtents.y,n=this.halfExtents.z,o=d,t=[new o(-e,-f,-n),new o(e,-f,-n),new o(e,f,-n),new o(-e,f,-n),new o(-e,-f,n),new o(e,-f,n),new o(e,f,n),new o(-e,f,n)],l=[[3,2,1,0],[4,5,6,7],[5,4,0,1],[2,3,7,6],[0,4,7,3],[1,2,6,5]],u=([new o(0,0,1),new o(0,1,0),new o(1,0,0)],new i(t,l));this.convexPolyhedronRepresentation=u,u.material=this.material},n.prototype.calculateLocalInertia=function(e,f){return f=f||new d,n.calculateInertia(this.halfExtents,e,f),f},n.calculateInertia=function(e,f,n){var o=e;n.x=1/12*f*(2*o.y*2*o.y+2*o.z*2*o.z),n.y=1/12*f*(2*o.x*2*o.x+2*o.z*2*o.z),n.z=1/12*f*(2*o.y*2*o.y+2*o.x*2*o.x)},n.prototype.getSideNormals=function(e,f){var n=e,o=this.halfExtents;if(n[0].set(o.x,0,0),n[1].set(0,o.y,0),n[2].set(0,0,o.z),n[3].set(-o.x,0,0),n[4].set(0,-o.y,0),n[5].set(0,0,-o.z),void 0!==f)for(var d=0;d!==n.length;d++)f.vmult(n[d],n[d]);return n},n.prototype.volume=function(){return 8*this.halfExtents.x*this.halfExtents.y*this.halfExtents.z},n.prototype.updateBoundingSphereRadius=function(){this.boundingSphereRadius=this.halfExtents.norm()};{var t=new d;new d}n.prototype.forEachWorldCorner=function(e,f,n){for(var o=this.halfExtents,d=[[o.x,o.y,o.z],[-o.x,o.y,o.z],[-o.x,-o.y,o.z],[-o.x,-o.y,-o.z],[o.x,-o.y,-o.z],[o.x,o.y,-o.z],[-o.x,o.y,-o.z],[o.x,-o.y,o.z]],i=0;it;t++){var i=l[t];f.vmult(i,i),e.vadd(i,i);var u=i.x,p=i.y,s=i.z;u>o.x&&(o.x=u),p>o.y&&(o.y=p),s>o.z&&(o.z=s),ua&&(a=w,c=r)}for(var b=[],m=n.faces[c],N=m.length,g=0;N>g;g++){var x=n.vertices[m[g]],j=new d;j.copy(x),i.vmult(j,j),o.vadd(j,j),b.push(j)}c>=0&&this.clipFaceAgainstHull(t,e,f,b,l,u,s)};var s=new d,y=new d,c=new d,a=new d,r=new d,w=new d;n.prototype.findSeparatingAxis=function(e,f,n,o,d,i,t,l){var u=s,p=y,b=c,m=a,N=r,g=w,x=Number.MAX_VALUE,j=this,v=0;if(j.uniqueAxes)for(var A=0;A!==j.uniqueAxes.length;A++){n.vmult(j.uniqueAxes[A],u);var C=j.testSepAxis(u,e,f,n,o,d);if(C===!1)return!1;x>C&&(x=C,i.copy(u))}else for(var O=t?t.length:j.faces.length,A=0;O>A;A++){var h=t?t[A]:A;u.copy(j.faceNormals[h]),n.vmult(u,u);var C=j.testSepAxis(u,e,f,n,o,d);if(C===!1)return!1;x>C&&(x=C,i.copy(u))}if(e.uniqueAxes)for(var A=0;A!==e.uniqueAxes.length;A++){d.vmult(e.uniqueAxes[A],p),v++;var C=j.testSepAxis(p,e,f,n,o,d);if(C===!1)return!1;x>C&&(x=C,i.copy(p))}else for(var k=l?l.length:e.faces.length,A=0;k>A;A++){var h=l?l[A]:A;p.copy(e.faceNormals[h]),d.vmult(p,p),v++;var C=j.testSepAxis(p,e,f,n,o,d);if(C===!1)return!1;x>C&&(x=C,i.copy(p))}for(var q=0;q!==j.uniqueEdges.length;q++){n.vmult(j.uniqueEdges[q],m);for(var z=0;z!==e.uniqueEdges.length;z++)if(d.vmult(e.uniqueEdges[z],N),m.cross(N,g),!g.almostZero()){g.normalize();var B=j.testSepAxis(g,e,f,n,o,d);if(B===!1)return!1;x>B&&(x=B,i.copy(g))}}return o.vsub(f,b),b.dot(i)>0&&i.negate(i),!0};var b=[],m=[];n.prototype.testSepAxis=function(e,f,o,d,i,t){var l=this;n.project(l,e,o,d,b),n.project(f,e,i,t,m);var u=b[0],p=b[1],s=m[0],y=m[1];if(y>u||p>s)return!1;var c=u-y,a=s-p,r=a>c?c:a;return r};var N=new d,g=new d;n.prototype.calculateLocalInertia=function(e,f){this.computeLocalAABB(N,g);var n=g.x-N.x,o=g.y-N.y,d=g.z-N.z;f.x=1/12*e*(2*o*2*o+2*d*2*d),f.y=1/12*e*(2*n*2*n+2*d*2*d),f.z=1/12*e*(2*o*2*o+2*n*2*n)},n.prototype.getPlaneConstantOfFace=function(e){var f=this.faces[e],n=this.faceNormals[e],o=this.vertices[f[0]],d=-n.dot(o);return d};var x=new d,j=new d,v=new d,A=new d,C=new d,O=new d,h=new d,k=new d;n.prototype.clipFaceAgainstHull=function(e,f,n,o,d,i,t){for(var l=x,u=j,p=v,s=A,y=C,c=O,a=h,r=k,w=this,b=[],m=o,N=b,g=-1,q=Number.MAX_VALUE,z=0;zB&&(q=B,g=z)}if(!(0>g)){var D=w.faces[g];D.connectedFaces=[];for(var E=0;EH;H++){var I=w.vertices[D[H]],J=w.vertices[D[(H+1)%G]];I.vsub(J,u),p.copy(u),n.vmult(p,p),f.vadd(p,p),s.copy(this.faceNormals[g]),n.vmult(s,s),f.vadd(s,s),p.cross(s,y),y.negate(y),c.copy(I),n.vmult(c,c),f.vadd(c,c);var K,L=(-c.dot(y),D.connectedFaces[H]);a.copy(this.faceNormals[L]);var M=this.getPlaneConstantOfFace(L);r.copy(a),n.vmult(r,r);var K=M-r.dot(f);for(this.clipFaceAgainstPlane(m,N,r,K);m.length;)m.shift();for(;N.length;)m.push(N.shift())}a.copy(this.faceNormals[g]);var M=this.getPlaneConstantOfFace(g);r.copy(a),n.vmult(r,r);for(var K=M-r.dot(f),E=0;E=P&&(console.log("clamped: depth="+P+" to minDist="+(d+"")),P=d),i>=P){var Q=m[E];if(0>=P){var R={point:Q,normal:r,depth:P};t.push(R)}}}}},n.prototype.clipFaceAgainstPlane=function(e,f,n,o){var i,t,l=e.length;if(2>l)return f;var u=e[e.length-1],p=e[0];i=n.dot(u)+o;for(var s=0;l>s;s++){if(p=e[s],t=n.dot(p)+o,0>i)if(0>t){var y=new d;y.copy(p),f.push(y)}else{var y=new d;u.lerp(p,i/(i-t),y),f.push(y)}else if(0>t){var y=new d;u.lerp(p,i/(i-t),y),f.push(y),f.push(p)}u=p,i=t}return f},n.prototype.computeWorldVertices=function(e,f){for(var n=this.vertices.length;this.worldVertices.lengthd;d++){var i=o[d];i.xf.x&&(f.x=i.x),i.yf.y&&(f.y=i.y),i.zf.z&&(f.z=i.z)}},n.prototype.computeWorldFaceNormals=function(e){for(var f=this.faceNormals.length;this.worldFaceNormals.lengthe&&(e=d)}this.boundingSphereRadius=Math.sqrt(e)};var q=new d;n.prototype.calculateWorldAABB=function(e,f,n,o){for(var d,i,t,l,u,p,s=this.vertices.length,y=this.vertices,c=0;s>c;c++){q.copy(y[c]),f.vmult(q,q),e.vadd(q,q);var a=q;a.xl||void 0===l)&&(l=a.x),a.yu||void 0===u)&&(u=a.y),a.zp||void 0===p)&&(p=a.z)}n.set(d,i,t),o.set(l,u,p)},n.prototype.volume=function(){return 4*Math.PI*this.boundingSphereRadius/3},n.prototype.getAveragePointLocal=function(e){e=e||new d;for(var f=this.vertices.length,n=this.vertices,o=0;f>o;o++)e.vadd(n[o],e);return e.mult(1/f,e),e},n.prototype.transformAllPoints=function(e,f){var n=this.vertices.length,o=this.vertices;if(f){for(var d=0;n>d;d++){var i=o[d];f.vmult(i,i)}for(var d=0;dd;d++){var i=o[d];i.vadd(e,i)}};var z=new d,B=new d,D=new d;n.prototype.pointIsInside=function(e){var f=this.vertices.length,n=this.vertices,o=this.faces,d=this.faceNormals,i=null,t=this.faces.length,l=z;this.getAveragePointLocal(l);for(var u=0;t>u;u++){var f=(this.faces[u].length,d[u]),p=n[o[u][0]],s=B;e.vsub(p,s);var y=f.dot(s),c=D;l.vsub(p,c);var a=f.dot(c);if(0>y&&a>0||y>0&&0>a)return!1}return i?1:-1};var E=(new d,new d),F=new d;n.project=function(e,f,n,o,d){var t=e.vertices.length,l=E,u=0,p=0,s=F,y=e.vertices;s.setZero(),i.vectorToLocalFrame(n,o,f,l),i.pointToLocalFrame(n,o,s,s);var c=s.dot(l);p=u=y[0].dot(l);for(var a=1;t>a;a++){var r=y[a].dot(l);r>u&&(u=r),p>r&&(p=r)}if(p-=c,u-=c,p>u){var w=p;p=u,u=w}d[0]=u,d[1]=p}},{"../math/Quaternion":28,"../math/Transform":29,"../math/Vec3":30,"./Shape":43}],39:[function(e,f){function n(e,f,n,t){var l=t,u=[],p=[],s=[],y=[],c=[],a=Math.cos,r=Math.sin;u.push(new d(f*a(0),f*r(0),.5*-n)),y.push(0),u.push(new d(e*a(0),e*r(0),.5*n)),c.push(1);for(var w=0;l>w;w++){var b=2*Math.PI/l*(w+1),m=2*Math.PI/l*(w+.5);l-1>w?(u.push(new d(f*a(b),f*r(b),.5*-n)),y.push(2*w+2),u.push(new d(e*a(b),e*r(b),.5*n)),c.push(2*w+3),s.push([2*w+2,2*w+3,2*w+1,2*w])):s.push([0,1,2*w+1,2*w]),(l%2===1||l/2>w)&&p.push(new d(a(m),r(m),0))}s.push(c),p.push(new d(0,0,1));for(var N=[],w=0;wd&&(f=d)}this.minValue=f},n.prototype.updateMaxValue=function(){for(var e=this.data,f=e[0][0],n=0;n!==e.length;n++)for(var o=0;o!==e[n].length;o++){var d=e[n][o];d>f&&(f=d)}this.maxValue=f},n.prototype.setHeightValueAtIndex=function(e,f,n){var o=this.data;o[e][f]=n,this.clearCachedConvexTrianglePillar(e,f,!1),e>0&&(this.clearCachedConvexTrianglePillar(e-1,f,!0),this.clearCachedConvexTrianglePillar(e-1,f,!1)),f>0&&(this.clearCachedConvexTrianglePillar(e,f-1,!0),this.clearCachedConvexTrianglePillar(e,f-1,!1)),f>0&&e>0&&this.clearCachedConvexTrianglePillar(e-1,f-1,!0)},n.prototype.getRectMinMax=function(e,f,n,o,d){d=d||[];for(var i=this.data,t=this.minValue,l=e;n>=l;l++)for(var u=f;o>=u;u++){var p=i[l][u];p>t&&(t=p)}d[0]=this.minValue,d[1]=t},n.prototype.getIndexOfPosition=function(e,f,n,o){var d=this.elementSize,i=this.data,t=Math.floor(e/d),l=Math.floor(f/d);return n[0]=t,n[1]=l,o&&(0>t&&(t=0),0>l&&(l=0),t>=i.length-1&&(t=i.length-1),l>=i[0].length-1&&(l=i[0].length-1)),0>t||0>l||t>=i.length-1||l>=i[0].length-1?!1:!0},n.prototype.getHeightAt=function(e,f,n){var o=[];this.getIndexOfPosition(e,f,o,n);var d=[];return this.getRectMinMax(o[0],o[1]+1,o[0],o[1]+1,d),(d[0]+d[1])/2},n.prototype.getCacheConvexTrianglePillarKey=function(e,f,n){return e+"_"+f+"_"+(n?1:0)},n.prototype.getCachedConvexTrianglePillar=function(e,f,n){return this._cachedPillars[this.getCacheConvexTrianglePillarKey(e,f,n)]},n.prototype.setCachedConvexTrianglePillar=function(e,f,n,o,d){this._cachedPillars[this.getCacheConvexTrianglePillarKey(e,f,n)]={convex:o,offset:d}},n.prototype.clearCachedConvexTrianglePillar=function(e,f,n){delete this._cachedPillars[this.getCacheConvexTrianglePillarKey(e,f,n)]},n.prototype.getConvexTrianglePillar=function(e,f,n){var o=this.pillarConvex,t=this.pillarOffset;if(this.cacheEnabled){var l=this.getCachedConvexTrianglePillar(e,f,n);if(l)return this.pillarConvex=l.convex,void(this.pillarOffset=l.offset);o=new d,t=new i,this.pillarConvex=o,this.pillarOffset=t}var l=this.data,u=this.elementSize,p=o.faces;o.vertices.length=6;for(var s=0;6>s;s++)o.vertices[s]||(o.vertices[s]=new i);p.length=5;for(var s=0;5>s;s++)p[s]||(p[s]=[]);var y=o.vertices,c=(Math.min(l[e][f],l[e+1][f],l[e][f+1],l[e+1][f+1])-this.minValue)/2+this.minValue;n?(t.set((e+.75)*u,(f+.75)*u,c),y[0].set(.25*u,.25*u,l[e+1][f+1]-c),y[1].set(-.75*u,.25*u,l[e][f+1]-c),y[2].set(.25*u,-.75*u,l[e+1][f]-c),y[3].set(.25*u,.25*u,-c-1),y[4].set(-.75*u,.25*u,-c-1),y[5].set(.25*u,-.75*u,-c-1),p[0][0]=0,p[0][1]=1,p[0][2]=2,p[1][0]=5,p[1][1]=4,p[1][2]=3,p[2][0]=2,p[2][1]=5,p[2][2]=3,p[2][3]=0,p[3][0]=3,p[3][1]=4,p[3][2]=1,p[3][3]=0,p[4][0]=1,p[4][1]=4,p[4][2]=5,p[4][3]=2):(t.set((e+.25)*u,(f+.25)*u,c),y[0].set(-.25*u,-.25*u,l[e][f]-c),y[1].set(.75*u,-.25*u,l[e+1][f]-c),y[2].set(-.25*u,.75*u,l[e][f+1]-c),y[3].set(-.25*u,-.25*u,-c-1),y[4].set(.75*u,-.25*u,-c-1),y[5].set(-.25*u,.75*u,-c-1),p[0][0]=0,p[0][1]=1,p[0][2]=2,p[1][0]=5,p[1][1]=4,p[1][2]=3,p[2][0]=0,p[2][1]=2,p[2][2]=5,p[2][3]=3,p[3][0]=1,p[3][1]=0,p[3][2]=3,p[3][3]=4,p[4][0]=4,p[4][1]=5,p[4][2]=2,p[4][3]=1),o.computeNormals(),o.computeEdges(),o.updateBoundingSphereRadius(),this.setCachedConvexTrianglePillar(e,f,n,o,t)},n.prototype.calculateLocalInertia=function(e,f){return f=f||new i,f.set(0,0,0),f},n.prototype.volume=function(){return Number.MAX_VALUE},n.prototype.calculateWorldAABB=function(e,f,n,o){n.set(-Number.MAX_VALUE,-Number.MAX_VALUE,-Number.MAX_VALUE),o.set(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE)},n.prototype.updateBoundingSphereRadius=function(){var e=this.data,f=this.elementSize;this.boundingSphereRadius=new i(e.length*f,e[0].length*f,Math.max(Math.abs(this.maxValue),Math.abs(this.minValue))).norm()}},{"../math/Vec3":30,"../utils/Utils":53,"./ConvexPolyhedron":38,"./Shape":43}],41:[function(e,f){function n(){o.call(this),this.type=o.types.PARTICLE}f.exports=n;var o=e("./Shape"),d=e("../math/Vec3");n.prototype=new o,n.prototype.constructor=n,n.prototype.calculateLocalInertia=function(e,f){return f=f||new d,f.set(0,0,0),f},n.prototype.volume=function(){return 0},n.prototype.updateBoundingSphereRadius=function(){this.boundingSphereRadius=0},n.prototype.calculateWorldAABB=function(e,f,n,o){n.copy(e),o.copy(e)}},{"../math/Vec3":30,"./Shape":43}],42:[function(e,f){function n(){o.call(this),this.type=o.types.PLANE,this.worldNormal=new d,this.worldNormalNeedsUpdate=!0,this.boundingSphereRadius=Number.MAX_VALUE}f.exports=n;var o=e("./Shape"),d=e("../math/Vec3");n.prototype=new o,n.prototype.constructor=n,n.prototype.computeWorldNormal=function(e){var f=this.worldNormal;f.set(0,0,1),e.vmult(f,f),this.worldNormalNeedsUpdate=!1},n.prototype.calculateLocalInertia=function(e,f){return f=f||new d},n.prototype.volume=function(){return Number.MAX_VALUE};var i=new d;n.prototype.calculateWorldAABB=function(e,f,n,o){i.set(0,0,1),f.vmult(i,i);var d=Number.MAX_VALUE;n.set(-d,-d,-d),o.set(d,d,d),1===i.x&&(o.x=e.x),1===i.y&&(o.y=e.y),1===i.z&&(o.z=e.z),-1===i.x&&(n.x=e.x),-1===i.y&&(n.y=e.y),-1===i.z&&(n.z=e.z)},n.prototype.updateBoundingSphereRadius=function(){this.boundingSphereRadius=Number.MAX_VALUE}},{"../math/Vec3":30,"./Shape":43}],43:[function(e,f){function n(){this.id=n.idCounter++,this.type=0,this.boundingSphereRadius=0,this.collisionResponse=!0,this.material=null}f.exports=n;{var n=e("./Shape");e("../math/Vec3"),e("../math/Quaternion"),e("../material/Material")}n.prototype.constructor=n,n.prototype.updateBoundingSphereRadius=function(){throw"computeBoundingSphereRadius() not implemented for shape type "+this.type},n.prototype.volume=function(){throw"volume() not implemented for shape type "+this.type},n.prototype.calculateLocalInertia=function(){throw"calculateLocalInertia() not implemented for shape type "+this.type},n.idCounter=0,n.types={SPHERE:1,PLANE:2,BOX:4,COMPOUND:8,CONVEXPOLYHEDRON:16,HEIGHTFIELD:32,PARTICLE:64,CYLINDER:128,TRIMESH:256}},{"../material/Material":25,"../math/Quaternion":28,"../math/Vec3":30,"./Shape":43}],44:[function(e,f){function n(e){if(o.call(this),this.radius=void 0!==e?Number(e):1,this.type=o.types.SPHERE,this.radius<0)throw new Error("The sphere radius cannot be negative.");this.updateBoundingSphereRadius()}f.exports=n;var o=e("./Shape"),d=e("../math/Vec3");n.prototype=new o,n.prototype.constructor=n,n.prototype.calculateLocalInertia=function(e,f){f=f||new d;var n=2*e*this.radius*this.radius/5;return f.x=n,f.y=n,f.z=n,f},n.prototype.volume=function(){return 4*Math.PI*this.radius/3},n.prototype.updateBoundingSphereRadius=function(){this.boundingSphereRadius=this.radius},n.prototype.calculateWorldAABB=function(e,f,n,o){for(var d=this.radius,i=["x","y","z"],t=0;td?d+"_"+i:i+"_"+d;e[f]=!0},n=0;nn.x&&(n.x=d.x),d.yn.y&&(n.y=d.y),d.zn.z&&(n.z=d.z)},n.prototype.updateAABB=function(){this.computeLocalAABB(this.aabb)},n.prototype.updateBoundingSphereRadius=function(){for(var e=0,f=this.vertices,n=new d,o=0,i=f.length/3;o!==i;o++){this.getVertex(o,n);var t=n.norm2();t>e&&(e=t)}this.boundingSphereRadius=Math.sqrt(e)};var g=(new d,new i),x=new t;n.prototype.calculateWorldAABB=function(e,f,n,o){var d=g,i=x;d.position=e,d.quaternion=f,this.aabb.toWorldFrame(d,i),n.copy(i.lowerBound),o.copy(i.upperBound)},n.prototype.volume=function(){return 4*Math.PI*this.boundingSphereRadius/3},n.createTorus=function(e,f,o,d,i){e=e||1,f=f||.5,o=o||8,d=d||6,i=i||2*Math.PI;for(var t=[],l=[],u=0;o>=u;u++)for(var p=0;d>=p;p++){var s=p/d*i,y=u/o*Math.PI*2,c=(e+f*Math.cos(y))*Math.cos(s),a=(e+f*Math.cos(y))*Math.sin(s),r=f*Math.sin(y);t.push(c,a,r)}for(var u=1;o>=u;u++)for(var p=1;d>=p;p++){var w=(d+1)*u+p-1,b=(d+1)*(u-1)+p-1,m=(d+1)*(u-1)+p,N=(d+1)*u+p;l.push(w,b,N),l.push(b,m,N)}return new n(t,l)}},{"../collision/AABB":3,"../math/Quaternion":28,"../math/Transform":29,"../math/Vec3":30,"../utils/Octree":50,"./Shape":43}],46:[function(e,f){function n(){o.call(this),this.iterations=10,this.tolerance=1e-7}f.exports=n;var o=(e("../math/Vec3"),e("../math/Quaternion"),e("./Solver"));n.prototype=new o;var d=[],i=[],t=[];n.prototype.solve=function(e,f){var n,o,l,u,p,s,y=0,c=this.iterations,a=this.tolerance*this.tolerance,r=this.equations,w=r.length,b=f.bodies,m=b.length,N=e;if(0!==w)for(var g=0;g!==m;g++)b[g].updateSolveMassProperties();var x=i,j=t,v=d; +x.length=w,j.length=w,v.length=w;for(var g=0;g!==w;g++){var A=r[g];v[g]=0,j[g]=A.computeB(N),x[g]=1/A.computeC()}if(0!==w){for(var g=0;g!==m;g++){var C=b[g],O=C.vlambda,h=C.wlambda;O.set(0,0,0),h&&h.set(0,0,0)}for(y=0;y!==c;y++){u=0;for(var k=0;k!==w;k++){var A=r[k];n=j[k],o=x[k],s=v[k],p=A.computeGWlambda(),l=o*(n-p-A.eps*s),s+lA.maxForce&&(l=A.maxForce-s),v[k]+=l,u+=l>0?l:-l,A.addToWlambda(l)}if(a>u*u)break}for(var g=0;g!==m;g++){var C=b[g],q=C.velocity,z=C.angularVelocity;q.vadd(C.vlambda,q),z&&z.vadd(C.wlambda,z)}}return y}},{"../math/Quaternion":28,"../math/Vec3":30,"./Solver":47}],47:[function(e,f){function n(){this.equations=[]}f.exports=n,n.prototype.solve=function(){return 0},n.prototype.addEquation=function(e){e.enabled&&this.equations.push(e)},n.prototype.removeEquation=function(e){var f=this.equations,n=f.indexOf(e);-1!==n&&f.splice(n,1)},n.prototype.removeAllEquations=function(){this.equations.length=0}},{}],48:[function(e,f){function n(e){for(l.call(this),this.iterations=10,this.tolerance=1e-7,this.subsolver=e,this.nodes=[],this.nodePool=[];this.nodePool.length<128;)this.nodePool.push(this.createNode())}function o(e){for(var f=e.length,n=0;n!==f;n++){var o=e[n];if(!(o.visited||o.body.type&c))return o}return!1}function d(e,f,n,d){for(a.push(e),e.visited=!0,f(e,n,d);a.length;)for(var i,t=a.pop();i=o(t.children);)i.visited=!0,f(i,n,d),a.push(i)}function i(e,f,n){f.push(e.body);for(var o=e.eqs.length,d=0;d!==o;d++){var i=e.eqs[d];-1===n.indexOf(i)&&n.push(i)}}function t(e,f){return f.id-e.id}f.exports=n;var l=(e("../math/Vec3"),e("../math/Quaternion"),e("./Solver")),u=e("../objects/Body");n.prototype=new l;var p=[],s=[],y={bodies:[]},c=u.STATIC,a=[];n.prototype.createNode=function(){return{body:null,children:[],eqs:[],visited:!1}},n.prototype.solve=function(e,f){for(var n=p,l=this.nodePool,u=f.bodies,c=this.equations,a=c.length,r=u.length,w=this.subsolver;l.lengthb;b++)n[b]=l[b];for(var b=0;b!==r;b++){var m=n[b];m.body=u[b],m.children.length=0,m.eqs.length=0,m.visited=!1}for(var N=0;N!==a;N++){var g=c[N],b=u.indexOf(g.bi),x=u.indexOf(g.bj),j=n[b],v=n[x];j.children.push(v),j.eqs.push(g),v.children.push(j),v.eqs.push(g)}var A,C=0,O=s;w.tolerance=this.tolerance,w.iterations=this.iterations;for(var h=y;A=o(n);){O.length=0,h.bodies.length=0,d(A,i,h.bodies,O);var k=O.length;O=O.sort(t);for(var b=0;b!==k;b++)w.addEquation(O[b]);{w.solve(e,h)}w.removeAllEquations(),C++}return C}},{"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"./Solver":47}],49:[function(e,f){var n=function(){};f.exports=n,n.prototype={constructor:n,addEventListener:function(e,f){void 0===this._listeners&&(this._listeners={});var n=this._listeners;return void 0===n[e]&&(n[e]=[]),-1===n[e].indexOf(f)&&n[e].push(f),this},hasEventListener:function(e,f){if(void 0===this._listeners)return!1;var n=this._listeners;return void 0!==n[e]&&-1!==n[e].indexOf(f)?!0:!1},removeEventListener:function(e,f){if(void 0===this._listeners)return this;var n=this._listeners;if(void 0===n[e])return this;var o=n[e].indexOf(f);return-1!==o&&n[e].splice(o,1),this},dispatchEvent:function(e){if(void 0===this._listeners)return this;var f=this._listeners,n=f[e.type];if(void 0!==n){e.target=this;for(var o=0,d=n.length;d>o;o++)n[o].call(this,e)}return this}}},{}],50:[function(e,f){function n(e){e=e||{},this.root=e.root||null,this.aabb=e.aabb?e.aabb.clone():new d,this.data=[],this.children=[]}function o(e,f){f=f||{},f.root=null,f.aabb=e,n.call(this,f),this.maxDepth="undefined"!=typeof f.maxDepth?f.maxDepth:8}var d=e("../collision/AABB"),i=e("../math/Vec3");f.exports=o,o.prototype=new n,n.prototype.reset=function(){this.children.length=this.data.length=0},n.prototype.insert=function(e,f,n){var o=this.data;if(n=n||0,!this.aabb.contains(e))return!1;var d=this.children;if(n<(this.maxDepth||this.root.maxDepth)){var i=!1;d.length||(this.subdivide(),i=!0);for(var t=0;8!==t;t++)if(d[t].insert(e,f,n+1))return!0;i&&(d.length=0)}return o.push(f),!0};var t=new i;n.prototype.subdivide=function(){var e=this.aabb,f=e.lowerBound,o=e.upperBound,l=this.children;l.push(new n({aabb:new d({lowerBound:new i(0,0,0)})}),new n({aabb:new d({lowerBound:new i(1,0,0)})}),new n({aabb:new d({lowerBound:new i(1,1,0)})}),new n({aabb:new d({lowerBound:new i(1,1,1)})}),new n({aabb:new d({lowerBound:new i(0,1,1)})}),new n({aabb:new d({lowerBound:new i(0,0,1)})}),new n({aabb:new d({lowerBound:new i(1,0,1)})}),new n({aabb:new d({lowerBound:new i(0,1,0)})})),o.vsub(f,t),t.scale(.5,t);for(var u=this.root||this,p=0;8!==p;p++){var s=l[p];s.root=u;var y=s.aabb.lowerBound;y.x*=t.x,y.y*=t.y,y.z*=t.z,y.vadd(f,y),y.vadd(t,s.aabb.upperBound)}},n.prototype.aabbQuery=function(e,f){for(var n=(this.data,this.children,[this]);n.length;){var o=n.pop();o.aabb.overlaps(e)&&Array.prototype.push.apply(f,o.data),Array.prototype.push.apply(n,o.children)}return f};var l=new d;n.prototype.rayQuery=function(e,f,n){return e.getAABB(l),l.toLocalFrame(f,l),this.aabbQuery(l,n),n},n.prototype.removeEmptyNodes=function(){for(var e=[this];e.length;){for(var f=e.pop(),n=f.children.length-1;n>=0;n--)f.children[n].data.length||f.children.splice(n,1);Array.prototype.push.apply(e,f.children)}}},{"../collision/AABB":3,"../math/Vec3":30}],51:[function(e,f){function n(){this.objects=[],this.type=Object}f.exports=n,n.prototype.release=function(){for(var e=arguments.length,f=0;f!==e;f++)this.objects.push(arguments[f])},n.prototype.get=function(){return 0===this.objects.length?this.constructObject():this.objects.pop()},n.prototype.constructObject=function(){throw new Error("constructObject() not implemented in this Pool subclass yet!")}},{}],52:[function(e,f){function n(){this.data={keys:[]}}f.exports=n,n.prototype.get=function(e,f){if(e>f){var n=f;f=e,e=n}return this.data[e+"-"+f]},n.prototype.set=function(e,f,n){if(e>f){var o=f;f=e,e=o}var d=e+"-"+f;this.get(e,f)||this.data.keys.push(d),this.data[d]=n},n.prototype.reset=function(){for(var e=this.data,f=e.keys;f.length>0;){var n=f.pop();delete e[n]}}},{}],53:[function(e,f){function n(){}f.exports=n,n.defaults=function(e,f){e=e||{};for(var n in f)n in e||(e[n]=f[n]);return e}},{}],54:[function(e,f){function n(){d.call(this),this.type=o}f.exports=n;var o=e("../math/Vec3"),d=e("./Pool");n.prototype=new d,n.prototype.constructObject=function(){return new o}},{"../math/Vec3":30,"./Pool":51}],55:[function(e,f){function n(e){this.contactPointPool=[],this.frictionEquationPool=[],this.result=[],this.frictionResult=[],this.v3pool=new s,this.world=e,this.currentContactMaterial=null,this.enableFrictionReduction=!1}function o(e,f,n){for(var o=null,d=e.length,i=0;i!==d;i++){var t=e[i],l=M;e[(i+1)%d].vsub(t,l);var u=P;l.cross(f,u);var p=Q;n.vsub(t,p);var s=u.dot(p);if(!(null===o||s>0&&o===!0||0>=s&&o===!1))return!1;null===o&&(o=s>0)}return!0}f.exports=n;var d=e("../collision/AABB"),i=e("../shapes/Shape"),t=e("../collision/Ray"),l=e("../math/Vec3"),u=e("../math/Transform"),p=(e("../shapes/ConvexPolyhedron"),e("../math/Quaternion")),s=(e("../solver/Solver"),e("../utils/Vec3Pool")),y=e("../equations/ContactEquation"),c=e("../equations/FrictionEquation");n.prototype.createContactEquation=function(e,f,n,o,d,i){var t;this.contactPointPool.length?(t=this.contactPointPool.pop(),t.bi=e,t.bj=f):t=new y(e,f),t.enabled=e.collisionResponse&&f.collisionResponse&&n.collisionResponse&&o.collisionResponse;var l=this.currentContactMaterial;t.restitution=l.restitution,t.setSpookParams(l.contactEquationStiffness,l.contactEquationRelaxation,this.world.dt);var u=n.material||e.material,p=o.material||f.material;return u&&p&&u.restitution>=0&&p.restitution>=0&&(t.restitution=u.restitution*p.restitution),t.si=d||n,t.sj=i||o,t},n.prototype.createFrictionEquationsFromContact=function(e,f){var n=e.bi,o=e.bj,d=e.si,i=e.sj,t=this.world,l=this.currentContactMaterial,u=l.friction,p=d.material||n.material,s=i.material||o.material;if(p&&s&&p.friction>=0&&s.friction>=0&&(u=p.friction*s.friction),u>0){var y=u*t.gravity.length(),a=n.invMass+o.invMass;a>0&&(a=1/a);var r=this.frictionEquationPool,w=r.length?r.pop():new c(n,o,y*a),b=r.length?r.pop():new c(n,o,y*a);return w.bi=b.bi=n,w.bj=b.bj=o,w.minForce=b.minForce=-y*a,w.maxForce=b.maxForce=y*a,w.ri.copy(e.ri),w.rj.copy(e.rj),b.ri.copy(e.ri),b.rj.copy(e.rj),e.ni.tangents(w.t,b.t),w.setSpookParams(l.frictionEquationStiffness,l.frictionEquationRelaxation,t.dt),b.setSpookParams(l.frictionEquationStiffness,l.frictionEquationRelaxation,t.dt),w.enabled=b.enabled=e.enabled,f.push(w,b),!0}return!1};var a=new l,r=new l,w=new l;n.prototype.createFrictionFromAverage=function(e){var f=this.result[this.result.length-1];if(this.createFrictionEquationsFromContact(f,this.frictionResult)&&1!==e){var n=this.frictionResult[this.frictionResult.length-2],o=this.frictionResult[this.frictionResult.length-1];a.setZero(),r.setZero(),w.setZero();for(var d=f.bi,i=(f.bj,0);i!==e;i++)f=this.result[this.result.length-1-i],f.bodyA!==d?(a.vadd(f.ni,a),r.vadd(f.ri,r),w.vadd(f.rj,w)):(a.vsub(f.ni,a),r.vadd(f.rj,r),w.vadd(f.ri,w));var t=1/e;r.scale(t,n.ri),w.scale(t,n.rj),o.ri.copy(n.ri),o.rj.copy(n.rj),a.normalize(),a.tangents(n.t,o.t)}};var b=new l,m=new l,N=new p,g=new p;n.prototype.getContacts=function(e,f,n,o,d,i,t){this.contactPointPool=d,this.frictionEquationPool=t,this.result=o,this.frictionResult=i;for(var l=N,u=g,p=b,s=m,y=0,c=e.length;y!==c;y++){var a=e[y],r=f[y],w=null;a.material&&r.material&&(w=n.getContactMaterial(a.material,r.material)||null);for(var x=0;xj.boundingSphereRadius+A.boundingSphereRadius)){var C=null;j.material&&A.material&&(C=n.getContactMaterial(j.material,A.material)||null),this.currentContactMaterial=C||w||n.defaultContactMaterial;var O=this[j.type|A.type];O&&(j.type=w){var b=this.createContactEquation(t,p,e,f);b.ni.copy(y);var m=v;y.scale(r.dot(y),m),s.vsub(m,m),b.ri.copy(m),b.ri.vsub(t.position,b.ri),b.rj.copy(s),b.rj.vsub(p.position,b.rj),this.result.push(b),this.createFrictionEquationsFromContact(b,this.frictionResult)}}};var A=new l,C=new l,O=(new l,new l),h=new l,k=new l,q=new l,z=new l,B=new l,D=new l,E=new l,F=new l,G=new l,H=new l,I=new d,J=[];n.prototype[i.types.SPHERE|i.types.TRIMESH]=n.prototype.sphereTrimesh=function(e,f,n,o,d,i,l,p){var s=k,y=q,c=z,a=B,r=D,w=E,b=I,m=h,N=C,g=J;u.pointToLocalFrame(o,i,n,r);var x=e.radius;b.lowerBound.set(r.x-x,r.y-x,r.z-x),b.upperBound.set(r.x+x,r.y+x,r.z+x),f.getTrianglesInAABB(b,g);for(var j=O,v=e.radius*e.radius,K=0;KL;L++)if(f.getVertex(f.indices[3*g[K]+L],j),j.vsub(r,N),N.norm2()<=v){m.copy(j),u.pointToWorldFrame(o,i,m,j),j.vsub(n,N);var M=this.createContactEquation(l,p,e,f);M.ni.copy(N),M.ni.normalize(),M.ri.copy(M.ni),M.ri.scale(e.radius,M.ri),M.ri.vadd(n,M.ri),M.ri.vsub(l.position,M.ri),M.rj.copy(j),M.rj.vsub(p.position,M.rj),this.result.push(M),this.createFrictionEquationsFromContact(M,this.frictionResult)}for(var K=0;KL;L++){f.getVertex(f.indices[3*g[K]+L],s),f.getVertex(f.indices[3*g[K]+(L+1)%3],y),y.vsub(s,c),r.vsub(y,w);var P=w.dot(c);r.vsub(s,w);var Q=w.dot(c);if(Q>0&&0>P){r.vsub(s,w),a.copy(c),a.normalize(),Q=w.dot(a),a.scale(Q,w),w.vadd(s,w);var R=w.distanceTo(r);if(RC&&C>0){var O=T,h=U;O.copy(p[(x+1)%3]),h.copy(p[(x+2)%3]);var k=O.norm(),q=h.norm();O.normalize(),h.normalize();var z=R.dot(O),B=R.dot(h);if(k>z&&z>-k&&q>B&&B>-q){var D=Math.abs(C-A-s);(null===g||g>D)&&(g=D,m=z,N=B,w=A,c.copy(v),a.copy(O),r.copy(h),b++)}}}if(b){y=!0;var E=this.createContactEquation(t,l,e,f);c.mult(-s,E.ri),E.ni.copy(c),E.ni.negate(E.ni),c.mult(w,c),a.mult(m,a),c.vadd(a,c),r.mult(N,r),c.vadd(r,E.rj),E.ri.vadd(n,E.ri),E.ri.vsub(t.position,E.ri),E.rj.vadd(o,E.rj),E.rj.vsub(l.position,E.rj),this.result.push(E),this.createFrictionEquationsFromContact(E,this.frictionResult)}for(var F=u.get(),G=W,H=0;2!==H&&!y;H++)for(var I=0;2!==I&&!y;I++)for(var J=0;2!==J&&!y;J++)if(F.set(0,0,0),H?F.vadd(p[0],F):F.vsub(p[0],F),I?F.vadd(p[1],F):F.vsub(p[1],F),J?F.vadd(p[2],F):F.vsub(p[2],F),o.vadd(F,G),G.vsub(n,G),G.norm2()_){y=!0;var ef=this.createContactEquation(t,l,e,f);L.vadd(M,ef.rj),ef.rj.copy(ef.rj),D.negate(ef.ni),ef.ni.normalize(),ef.ri.copy(ef.rj),ef.ri.vadd(o,ef.ri),ef.ri.vsub(n,ef.ri),ef.ri.normalize(),ef.ri.mult(s,ef.ri),ef.ri.vadd(n,ef.ri),ef.ri.vsub(t.position,ef.ri),ef.rj.vadd(o,ef.rj),ef.rj.vsub(l.position,ef.rj),this.result.push(ef),this.createFrictionEquationsFromContact(ef,this.frictionResult)}}u.release(K,L,E,M,D)};var $=new l,_=new l,ef=new l,ff=new l,nf=new l,of=new l,df=new l,tf=new l,lf=new l,uf=new l;n.prototype[i.types.SPHERE|i.types.CONVEXPOLYHEDRON]=n.prototype.sphereConvex=function(e,f,n,d,i,t,l,u){var p=this.v3pool;n.vsub(d,$);for(var s=f.faceNormals,y=f.faces,c=f.vertices,a=e.radius,r=0;r!==c.length;r++){var w=c[r],b=nf;t.vmult(w,b),d.vadd(b,b);var m=ff;if(b.vsub(n,m),m.norm2()k&&q.dot(A)>0){for(var z=[],B=0,D=v.length;B!==D;B++){var E=p.get();t.vmult(c[v[B]],E),d.vadd(E,E),z.push(E)}if(o(z,A,n)){g=!0;var N=this.createContactEquation(l,u,e,f);A.mult(-a,N.ri),A.negate(N.ni);var F=p.get();A.mult(-k,F);var G=p.get();A.mult(-a,G),n.vsub(d,N.rj),N.rj.vadd(G,N.rj),N.rj.vadd(F,N.rj),N.rj.vadd(d,N.rj),N.rj.vsub(u.position,N.rj),N.ri.vadd(n,N.ri),N.ri.vsub(l.position,N.ri),p.release(F),p.release(G),this.result.push(N),this.createFrictionEquationsFromContact(N,this.frictionResult);for(var B=0,H=z.length;B!==H;B++)p.release(z[B]);return}for(var B=0;B!==v.length;B++){var I=p.get(),J=p.get();t.vmult(c[v[(B+1)%v.length]],I),t.vmult(c[v[(B+2)%v.length]],J),d.vadd(I,I),d.vadd(J,J);var K=_;J.vsub(I,K);var L=ef;K.unit(L);var M=p.get(),P=p.get();n.vsub(I,P);var Q=P.dot(L);L.mult(Q,M),M.vadd(I,M);var R=p.get();if(M.vsub(n,R),Q>0&&Q*Q=a){var r=this.createContactEquation(t,l,e,f),w=cf;p.mult(p.dot(y),w),u.vsub(w,w),w.vsub(n,r.ri),r.ni.copy(p),u.vsub(o,r.rj),r.ri.vadd(n,r.ri),r.ri.vsub(t.position,r.ri),r.rj.vadd(o,r.rj),r.rj.vsub(l.position,r.rj),this.result.push(r),s++,this.enableFrictionReduction||this.createFrictionEquationsFromContact(r,this.frictionResult)}}this.enableFrictionReduction&&s&&this.createFrictionFromAverage(s)};var af=new l,rf=new l;n.prototype[i.types.CONVEXPOLYHEDRON]=n.prototype.convexConvex=function(e,f,n,o,d,i,t,l,u,p,s,y){var c=af;if(!(n.distanceTo(o)>e.boundingSphereRadius+f.boundingSphereRadius)&&e.findSeparatingAxis(f,n,d,o,i,c,s,y)){var a=[],r=rf;e.clipAgainstHull(n,d,f,o,i,c,-100,100,a);for(var w=0,b=0;b!==a.length;b++){var m=this.createContactEquation(t,l,e,f,u,p),N=m.ri,g=m.rj;c.negate(m.ni),a[b].normal.negate(r),r.mult(a[b].depth,r),a[b].point.vadd(r,N),g.copy(a[b].point),N.vsub(n,N),g.vsub(o,g),N.vadd(n,N),N.vsub(t.position,N),g.vadd(o,g),g.vsub(l.position,g),this.result.push(m),w++,this.enableFrictionReduction||this.createFrictionEquationsFromContact(m,this.frictionResult)}this.enableFrictionReduction&&w&&this.createFrictionFromAverage(w)}};var wf=new l,bf=new l,mf=new l;n.prototype[i.types.PLANE|i.types.PARTICLE]=n.prototype.planeParticle=function(e,f,n,o,d,i,t,l){var u=wf;u.set(0,0,1),t.quaternion.vmult(u,u);var p=bf;o.vsub(t.position,p);var s=u.dot(p);if(0>=s){var y=this.createContactEquation(l,t,f,e);y.ni.copy(u),y.ni.negate(y.ni),y.ri.set(0,0,0);var c=mf;u.mult(u.dot(o),c),o.vsub(c,c),y.rj.copy(c),this.result.push(y),this.createFrictionEquationsFromContact(y,this.frictionResult)}};var Nf=new l;n.prototype[i.types.PARTICLE|i.types.SPHERE]=n.prototype.sphereParticle=function(e,f,n,o,d,i,t,l){var u=Nf;u.set(0,0,1),o.vsub(n,u);var p=u.norm2();if(p<=e.radius*e.radius){var s=this.createContactEquation(l,t,f,e);u.normalize(),s.rj.copy(u),s.rj.mult(e.radius,s.rj),s.ni.copy(u),s.ni.negate(s.ni),s.ri.set(0,0,0),this.result.push(s),this.createFrictionEquationsFromContact(s,this.frictionResult)}};var gf=new p,xf=new l,jf=(new l,new l),vf=new l,Af=new l;n.prototype[i.types.PARTICLE|i.types.CONVEXPOLYHEDRON]=n.prototype.convexParticle=function(e,f,n,o,d,i,t,l){var u=-1,p=jf,s=Af,y=null,c=0,a=xf;if(a.copy(o),a.vsub(n,a),d.conjugate(gf),gf.vmult(a,a),e.pointIsInside(a)){e.worldVerticesNeedsUpdate&&e.computeWorldVertices(n,d),e.worldFaceNormalsNeedsUpdate&&e.computeWorldFaceNormals(d);for(var r=0,w=e.faces.length;r!==w;r++){var b=[e.worldVertices[e.faces[r][0]]],m=e.worldFaceNormals[r];o.vsub(b[0],vf);var N=-m.dot(vf);(null===y||Math.abs(N)b||0>N||w>p.length||m>p[0].length)){0>w&&(w=0),0>b&&(b=0),0>m&&(m=0),0>N&&(N=0),w>=p.length&&(w=p.length-1),b>=p.length&&(b=p.length-1),N>=p[0].length&&(N=p[0].length-1),m>=p[0].length&&(m=p[0].length-1);var g=[];f.getRectMinMax(w,m,b,N,g);var x=g[0],j=g[1];if(!(r.z-y>j||r.z+yv;v++)for(var A=m;N>A;A++)f.getConvexTrianglePillar(v,A,!1),u.pointToWorldFrame(o,i,f.pillarOffset,c),n.distanceTo(c)w||0>m||r>p.length||m>p[0].length)){0>r&&(r=0),0>w&&(w=0),0>b&&(b=0),0>m&&(m=0),r>=p.length&&(r=p.length-1),w>=p.length&&(w=p.length-1),m>=p[0].length&&(m=p[0].length-1),b>=p[0].length&&(b=p[0].length-1);var N=[];f.getRectMinMax(r,b,w,m,N);var g=N[0],x=N[1];if(!(a.z-s>x||a.z+sv;v++)for(var A=b;m>A;A++){var C=j.length;f.getConvexTrianglePillar(v,A,!1),u.pointToWorldFrame(o,i,f.pillarOffset,c),n.distanceTo(c)2)return}}}},{"../collision/AABB":3,"../collision/Ray":9,"../equations/ContactEquation":19,"../equations/FrictionEquation":21,"../math/Quaternion":28,"../math/Transform":29,"../math/Vec3":30,"../shapes/ConvexPolyhedron":38,"../shapes/Shape":43,"../solver/Solver":47,"../utils/Vec3Pool":54}],56:[function(e,f){function n(){u.apply(this),this.dt=-1,this.allowSleep=!1,this.contacts=[],this.frictionEquations=[],this.quatNormalizeSkip=0,this.quatNormalizeFast=!1,this.time=0,this.stepnumber=0,this.default_dt=1/60,this.nextId=0,this.gravity=new d,this.broadphase=new m,this.bodies=[],this.solver=new t,this.constraints=[],this.narrowphase=new l(this),this.collisionMatrix=new p,this.collisionMatrixPrevious=new p,this.materials=[],this.contactmaterials=[],this.contactMaterialTable=new a,this.defaultMaterial=new s("default"),this.defaultContactMaterial=new y(this.defaultMaterial,this.defaultMaterial,{friction:.3,restitution:0}),this.doProfiling=!1,this.profile={solve:0,makeContactConstraints:0,broadphase:0,integrate:0,narrowphase:0},this.subsystems=[],this.addBodyEvent={type:"addBody",body:null},this.removeBodyEvent={type:"removeBody",body:null}}f.exports=n;var o=e("../shapes/Shape"),d=e("../math/Vec3"),i=e("../math/Quaternion"),t=e("../solver/GSSolver"),l=(e("../utils/Vec3Pool"),e("../equations/ContactEquation"),e("../equations/FrictionEquation"),e("./Narrowphase")),u=e("../utils/EventTarget"),p=e("../collision/ArrayCollisionMatrix"),s=e("../material/Material"),y=e("../material/ContactMaterial"),c=e("../objects/Body"),a=e("../utils/TupleDictionary"),r=e("../collision/RaycastResult"),w=e("../collision/AABB"),b=e("../collision/Ray"),m=e("../collision/NaiveBroadphase");n.prototype=new u;var N=(new w,new b);if(n.prototype.getContactMaterial=function(e,f){return this.contactMaterialTable.get(e.id,f.id)},n.prototype.numObjects=function(){return this.bodies.length},n.prototype.collisionMatrixTick=function(){var e=this.collisionMatrixPrevious;this.collisionMatrixPrevious=this.collisionMatrix,this.collisionMatrix=e,this.collisionMatrix.reset()},n.prototype.add=n.prototype.addBody=function(e){-1===this.bodies.indexOf(e)&&(e.index=this.bodies.length,this.bodies.push(e),e.world=this,e.initPosition.copy(e.position),e.initVelocity.copy(e.velocity),e.timeLastSleepy=this.time,e instanceof c&&(e.initAngularVelocity.copy(e.angularVelocity),e.initQuaternion.copy(e.quaternion)),this.collisionMatrix.setNumObjects(this.bodies.length),this.addBodyEvent.body=e,this.dispatchEvent(this.addBodyEvent))},n.prototype.addConstraint=function(e){this.constraints.push(e)},n.prototype.removeConstraint=function(e){var f=this.constraints.indexOf(e);-1!==f&&this.constraints.splice(f,1)},n.prototype.rayTest=function(e,f,n){n instanceof r?this.raycastClosest(e,f,{skipBackfaces:!0},n):this.raycastAll(e,f,{skipBackfaces:!0},n)},n.prototype.raycastAll=function(e,f,n,o){return n.mode=b.ALL,n.from=e,n.to=f,n.callback=o,N.intersectWorld(this,n)},n.prototype.raycastAny=function(e,f,n,o){return n.mode=b.ANY,n.from=e,n.to=f,n.result=o,N.intersectWorld(this,n)},n.prototype.raycastClosest=function(e,f,n,o){return n.mode=b.CLOSEST,n.from=e,n.to=f,n.result=o,N.intersectWorld(this,n)},n.prototype.remove=function(e){e.world=null;var f=this.bodies.length-1,n=this.bodies,o=n.indexOf(e);if(-1!==o){n.splice(o,1);for(var d=0;d!==n.length;d++)n[d].index=d;this.collisionMatrix.setNumObjects(f),this.removeBodyEvent.body=e,this.dispatchEvent(this.removeBodyEvent)}},n.prototype.removeBody=n.prototype.remove,n.prototype.addMaterial=function(e){this.materials.push(e)},n.prototype.addContactMaterial=function(e){this.contactmaterials.push(e),this.contactMaterialTable.set(e.materials[0].id,e.materials[1].id,e)},"undefined"==typeof performance&&(performance={}),!performance.now){var g=Date.now();performance.timing&&performance.timing.navigationStart&&(g=performance.timing.navigationStart),performance.now=function(){return Date.now()-g}}var x=new d;n.prototype.step=function(e,f,n){if(n=n||10,f=f||0,0===f)this.internalStep(e),this.time+=e;else{var o=Math.floor((this.time+f)/e)-Math.floor(this.time/e);o=Math.min(o,n);for(var d=performance.now(),i=0;i!==o&&(this.internalStep(e),!(performance.now()-d>1e3*e));i++);this.time+=f;for(var t=this.time%e,l=t/e,u=x,p=this.bodies,s=0;s!==p.length;s++){var y=p[s];y.type!==c.STATIC&&y.sleepState!==c.SLEEPING?(y.position.vsub(y.previousPosition,u),u.scale(l,u),y.position.vadd(u,y.interpolatedPosition)):(y.interpolatedPosition.copy(y.position),y.interpolatedQuaternion.copy(y.quaternion))}}};var j={type:"postStep"},v={type:"preStep"},A={type:"collide",body:null,contact:null},C=[],O=[],h=[],k=[],q=(new d,new d,new d,new d,new d,new d,new d,new d,new d,new i,new i),z=new i,B=new d;n.prototype.internalStep=function(e){this.dt=e;var f,n=this.contacts,d=h,i=k,t=this.numObjects(),l=this.bodies,u=this.solver,p=this.gravity,s=this.doProfiling,y=this.profile,a=c.DYNAMIC,r=this.constraints,w=O,b=(p.norm(),p.x),m=p.y,N=p.z,g=0;for(s&&(f=performance.now()),g=0;g!==t;g++){var x=l[g];if(x.type&a){var D=x.force,E=x.mass;D.x+=E*b,D.y+=E*m,D.z+=E*N}}for(var g=0,F=this.subsystems.length;g!==F;g++)this.subsystems[g].update();s&&(f=performance.now()),d.length=0,i.length=0,this.broadphase.collisionPairs(this,d,i),s&&(y.broadphase=performance.now()-f);var G=r.length;for(g=0;g!==G;g++){var H=r[g];if(!H.collideConnected)for(var I=d.length-1;I>=0;I-=1)(H.bodyA===d[I]&&H.bodyB===i[I]||H.bodyB===d[I]&&H.bodyA===i[I])&&(d.splice(I,1),i.splice(I,1))}this.collisionMatrixTick(),s&&(f=performance.now());var J=C,K=n.length;for(g=0;g!==K;g++)J.push(n[g]);n.length=0;var L=this.frictionEquations.length;for(g=0;g!==L;g++)w.push(this.frictionEquations[g]);this.frictionEquations.length=0,this.narrowphase.getContacts(d,i,this,n,J,this.frictionEquations,w),s&&(y.narrowphase=performance.now()-f),s&&(f=performance.now());for(var g=0;g=0&&R.material.friction>=0&&(S=x.material.friction*R.material.friction),x.material.restitution>=0&&R.material.restitution>=0&&(H.restitution=x.material.restitution*R.material.restitution)),u.addEquation(H),x.allowSleep&&x.type===c.DYNAMIC&&x.sleepState===c.SLEEPING&&R.sleepState===c.AWAKE&&R.type!==c.STATIC){var T=R.velocity.norm2()+R.angularVelocity.norm2(),U=Math.pow(R.sleepSpeedLimit,2); +T>=2*U&&(x._wakeUpAfterNarrowphase=!0)}if(R.allowSleep&&R.type===c.DYNAMIC&&R.sleepState===c.SLEEPING&&x.sleepState===c.AWAKE&&x.type!==c.STATIC){var V=x.velocity.norm2()+x.angularVelocity.norm2(),W=Math.pow(x.sleepSpeedLimit,2);V>=2*W&&(R._wakeUpAfterNarrowphase=!0)}this.collisionMatrix.set(x,R,!0),this.collisionMatrixPrevious.get(x,R)||(A.body=R,A.contact=H,x.dispatchEvent(A),A.body=x,R.dispatchEvent(A))}for(s&&(y.makeContactConstraints=performance.now()-f,f=performance.now()),g=0;g!==t;g++){var x=l[g];x._wakeUpAfterNarrowphase&&(x.wakeUp(),x._wakeUpAfterNarrowphase=!1)}var G=r.length;for(g=0;g!==G;g++){var H=r[g];H.update();for(var I=0,X=H.equations.length;I!==X;I++){var Y=H.equations[I];u.addEquation(Y)}}u.solve(e,this),s&&(y.solve=performance.now()-f),u.removeAllEquations();var Z=Math.pow;for(g=0;g!==t;g++){var x=l[g];if(x.type&a){var $=Z(1-x.linearDamping,e),_=x.velocity;_.mult($,_);var ef=x.angularVelocity;if(ef){var ff=Z(1-x.angularDamping,e);ef.mult(ff,ef)}}}for(this.dispatchEvent(v),g=0;g!==t;g++){var x=l[g];x.preStep&&x.preStep.call(x)}s&&(f=performance.now());{var nf=q,of=z,df=this.stepnumber,tf=c.DYNAMIC|c.KINEMATIC,lf=df%(this.quatNormalizeSkip+1)===0,uf=this.quatNormalizeFast,pf=.5*e;o.types.PLANE,o.types.CONVEXPOLYHEDRON}for(g=0;g!==t;g++){var sf=l[g],yf=sf.force,cf=sf.torque;if(sf.type&tf&&sf.sleepState!==c.SLEEPING){var af=sf.velocity,rf=sf.angularVelocity,wf=sf.position,bf=sf.quaternion,mf=sf.invMass,Nf=sf.invInertiaWorld;af.x+=yf.x*mf*e,af.y+=yf.y*mf*e,af.z+=yf.z*mf*e,sf.angularVelocity&&(Nf.vmult(cf,B),B.mult(e,B),B.vadd(rf,rf)),wf.x+=af.x*e,wf.y+=af.y*e,wf.z+=af.z*e,sf.angularVelocity&&(nf.set(rf.x,rf.y,rf.z,0),nf.mult(bf,of),bf.x+=pf*of.x,bf.y+=pf*of.y,bf.z+=pf*of.z,bf.w+=pf*of.w,lf&&(uf?bf.normalizeFast():bf.normalize())),sf.aabb&&(sf.aabbNeedsUpdate=!0),sf.updateInertiaWorld&&sf.updateInertiaWorld()}}for(this.clearForces(),this.broadphase.dirty=!0,s&&(y.integrate=performance.now()-f),this.time+=e,this.stepnumber+=1,this.dispatchEvent(j),g=0;g!==t;g++){var x=l[g],gf=x.postStep;gf&&gf.call(x)}if(this.allowSleep)for(g=0;g!==t;g++)l[g].sleepTick(this.time)},n.prototype.clearForces=function(){for(var e=this.bodies,f=e.length,n=0;n!==f;n++){{var o=e[n];o.force,o.torque}o.force.set(0,0,0),o.torque.set(0,0,0)}}},{"../collision/AABB":3,"../collision/ArrayCollisionMatrix":4,"../collision/NaiveBroadphase":7,"../collision/Ray":9,"../collision/RaycastResult":10,"../equations/ContactEquation":19,"../equations/FrictionEquation":21,"../material/ContactMaterial":24,"../material/Material":25,"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"../shapes/Shape":43,"../solver/GSSolver":46,"../utils/EventTarget":49,"../utils/TupleDictionary":52,"../utils/Vec3Pool":54,"./Narrowphase":55}]},{},[2])(2)}); \ No newline at end of file diff --git a/daydream/public/libs/createjs-2015.11.26.min.js b/daydream/public/libs/createjs-2015.11.26.min.js new file mode 100644 index 000000000..ba15ff104 --- /dev/null +++ b/daydream/public/libs/createjs-2015.11.26.min.js @@ -0,0 +1,17 @@ +/*! +* @license CreateJS +* Visit http://createjs.com/ for documentation, updates and examples. +* +* Copyright (c) 2011-2015 gskinner.com, inc. +* +* Distributed under the terms of the MIT license. +* http://www.opensource.org/licenses/mit-license.html +* +* This notice shall be included in all copies or substantial portions of the Software. +*/ +this.createjs=this.createjs||{},createjs.extend=function(a,b){"use strict";function c(){this.constructor=a}return c.prototype=b.prototype,a.prototype=new c},this.createjs=this.createjs||{},createjs.promote=function(a,b){"use strict";var c=a.prototype,d=Object.getPrototypeOf&&Object.getPrototypeOf(c)||c.__proto__;if(d){c[(b+="_")+"constructor"]=d.constructor;for(var e in d)c.hasOwnProperty(e)&&"function"==typeof d[e]&&(c[b+e]=d[e])}return a},this.createjs=this.createjs||{},createjs.indexOf=function(a,b){"use strict";for(var c=0,d=a.length;d>c;c++)if(b===a[c])return c;return-1},this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.type=a,this.target=null,this.currentTarget=null,this.eventPhase=0,this.bubbles=!!b,this.cancelable=!!c,this.timeStamp=(new Date).getTime(),this.defaultPrevented=!1,this.propagationStopped=!1,this.immediatePropagationStopped=!1,this.removed=!1}var b=a.prototype;b.preventDefault=function(){this.defaultPrevented=this.cancelable&&!0},b.stopPropagation=function(){this.propagationStopped=!0},b.stopImmediatePropagation=function(){this.immediatePropagationStopped=this.propagationStopped=!0},b.remove=function(){this.removed=!0},b.clone=function(){return new a(this.type,this.bubbles,this.cancelable)},b.set=function(a){for(var b in a)this[b]=a[b];return this},b.toString=function(){return"[Event (type="+this.type+")]"},createjs.Event=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(){this._listeners=null,this._captureListeners=null}var b=a.prototype;a.initialize=function(a){a.addEventListener=b.addEventListener,a.on=b.on,a.removeEventListener=a.off=b.removeEventListener,a.removeAllEventListeners=b.removeAllEventListeners,a.hasEventListener=b.hasEventListener,a.dispatchEvent=b.dispatchEvent,a._dispatchEvent=b._dispatchEvent,a.willTrigger=b.willTrigger},b.addEventListener=function(a,b,c){var d;d=c?this._captureListeners=this._captureListeners||{}:this._listeners=this._listeners||{};var e=d[a];return e&&this.removeEventListener(a,b,c),e=d[a],e?e.push(b):d[a]=[b],b},b.on=function(a,b,c,d,e,f){return b.handleEvent&&(c=c||b,b=b.handleEvent),c=c||this,this.addEventListener(a,function(a){b.call(c,a,e),d&&a.remove()},f)},b.removeEventListener=function(a,b,c){var d=c?this._captureListeners:this._listeners;if(d){var e=d[a];if(e)for(var f=0,g=e.length;g>f;f++)if(e[f]==b){1==g?delete d[a]:e.splice(f,1);break}}},b.off=b.removeEventListener,b.removeAllEventListeners=function(a){a?(this._listeners&&delete this._listeners[a],this._captureListeners&&delete this._captureListeners[a]):this._listeners=this._captureListeners=null},b.dispatchEvent=function(a,b,c){if("string"==typeof a){var d=this._listeners;if(!(b||d&&d[a]))return!0;a=new createjs.Event(a,b,c)}else a.target&&a.clone&&(a=a.clone());try{a.target=this}catch(e){}if(a.bubbles&&this.parent){for(var f=this,g=[f];f.parent;)g.push(f=f.parent);var h,i=g.length;for(h=i-1;h>=0&&!a.propagationStopped;h--)g[h]._dispatchEvent(a,1+(0==h));for(h=1;i>h&&!a.propagationStopped;h++)g[h]._dispatchEvent(a,3)}else this._dispatchEvent(a,2);return!a.defaultPrevented},b.hasEventListener=function(a){var b=this._listeners,c=this._captureListeners;return!!(b&&b[a]||c&&c[a])},b.willTrigger=function(a){for(var b=this;b;){if(b.hasEventListener(a))return!0;b=b.parent}return!1},b.toString=function(){return"[EventDispatcher]"},b._dispatchEvent=function(a,b){var c,d=1==b?this._captureListeners:this._listeners;if(a&&d){var e=d[a.type];if(!e||!(c=e.length))return;try{a.currentTarget=this}catch(f){}try{a.eventPhase=b}catch(f){}a.removed=!1,e=e.slice();for(var g=0;c>g&&!a.immediatePropagationStopped;g++){var h=e[g];h.handleEvent?h.handleEvent(a):h(a),a.removed&&(this.off(a.type,h,1==b),a.removed=!1)}}},createjs.EventDispatcher=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(){throw"Ticker cannot be instantiated."}a.RAF_SYNCHED="synched",a.RAF="raf",a.TIMEOUT="timeout",a.useRAF=!1,a.timingMode=null,a.maxDelta=0,a.paused=!1,a.removeEventListener=null,a.removeAllEventListeners=null,a.dispatchEvent=null,a.hasEventListener=null,a._listeners=null,createjs.EventDispatcher.initialize(a),a._addEventListener=a.addEventListener,a.addEventListener=function(){return!a._inited&&a.init(),a._addEventListener.apply(a,arguments)},a._inited=!1,a._startTime=0,a._pausedTime=0,a._ticks=0,a._pausedTicks=0,a._interval=50,a._lastTime=0,a._times=null,a._tickTimes=null,a._timerId=null,a._raf=!0,a.setInterval=function(b){a._interval=b,a._inited&&a._setupTick()},a.getInterval=function(){return a._interval},a.setFPS=function(b){a.setInterval(1e3/b)},a.getFPS=function(){return 1e3/a._interval};try{Object.defineProperties(a,{interval:{get:a.getInterval,set:a.setInterval},framerate:{get:a.getFPS,set:a.setFPS}})}catch(b){console.log(b)}a.init=function(){a._inited||(a._inited=!0,a._times=[],a._tickTimes=[],a._startTime=a._getTime(),a._times.push(a._lastTime=0),a.interval=a._interval)},a.reset=function(){if(a._raf){var b=window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||window.oCancelAnimationFrame||window.msCancelAnimationFrame;b&&b(a._timerId)}else clearTimeout(a._timerId);a.removeAllEventListeners("tick"),a._timerId=a._times=a._tickTimes=null,a._startTime=a._lastTime=a._ticks=0,a._inited=!1},a.getMeasuredTickTime=function(b){var c=0,d=a._tickTimes;if(!d||d.length<1)return-1;b=Math.min(d.length,b||0|a.getFPS());for(var e=0;b>e;e++)c+=d[e];return c/b},a.getMeasuredFPS=function(b){var c=a._times;return!c||c.length<2?-1:(b=Math.min(c.length-1,b||0|a.getFPS()),1e3/((c[0]-c[b])/b))},a.setPaused=function(b){a.paused=b},a.getPaused=function(){return a.paused},a.getTime=function(b){return a._startTime?a._getTime()-(b?a._pausedTime:0):-1},a.getEventTime=function(b){return a._startTime?(a._lastTime||a._startTime)-(b?a._pausedTime:0):-1},a.getTicks=function(b){return a._ticks-(b?a._pausedTicks:0)},a._handleSynch=function(){a._timerId=null,a._setupTick(),a._getTime()-a._lastTime>=.97*(a._interval-1)&&a._tick()},a._handleRAF=function(){a._timerId=null,a._setupTick(),a._tick()},a._handleTimeout=function(){a._timerId=null,a._setupTick(),a._tick()},a._setupTick=function(){if(null==a._timerId){var b=a.timingMode||a.useRAF&&a.RAF_SYNCHED;if(b==a.RAF_SYNCHED||b==a.RAF){var c=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame;if(c)return a._timerId=c(b==a.RAF?a._handleRAF:a._handleSynch),void(a._raf=!0)}a._raf=!1,a._timerId=setTimeout(a._handleTimeout,a._interval)}},a._tick=function(){var b=a.paused,c=a._getTime(),d=c-a._lastTime;if(a._lastTime=c,a._ticks++,b&&(a._pausedTicks++,a._pausedTime+=d),a.hasEventListener("tick")){var e=new createjs.Event("tick"),f=a.maxDelta;e.delta=f&&d>f?f:d,e.paused=b,e.time=c,e.runTime=c-a._pausedTime,a.dispatchEvent(e)}for(a._tickTimes.unshift(a._getTime()-c);a._tickTimes.length>100;)a._tickTimes.pop();for(a._times.unshift(c);a._times.length>100;)a._times.pop()};var c=window.performance&&(performance.now||performance.mozNow||performance.msNow||performance.oNow||performance.webkitNow);a._getTime=function(){return(c&&c.call(performance)||(new Date).getTime())-a._startTime},createjs.Ticker=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(){throw"UID cannot be instantiated"}a._nextID=0,a.get=function(){return a._nextID++},createjs.UID=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c,d,e,f,g,h,i,j,k){this.Event_constructor(a,b,c),this.stageX=d,this.stageY=e,this.rawX=null==i?d:i,this.rawY=null==j?e:j,this.nativeEvent=f,this.pointerID=g,this.primary=!!h,this.relatedTarget=k}var b=createjs.extend(a,createjs.Event);b._get_localX=function(){return this.currentTarget.globalToLocal(this.rawX,this.rawY).x},b._get_localY=function(){return this.currentTarget.globalToLocal(this.rawX,this.rawY).y},b._get_isTouch=function(){return-1!==this.pointerID};try{Object.defineProperties(b,{localX:{get:b._get_localX},localY:{get:b._get_localY},isTouch:{get:b._get_isTouch}})}catch(c){}b.clone=function(){return new a(this.type,this.bubbles,this.cancelable,this.stageX,this.stageY,this.nativeEvent,this.pointerID,this.primary,this.rawX,this.rawY)},b.toString=function(){return"[MouseEvent (type="+this.type+" stageX="+this.stageX+" stageY="+this.stageY+")]"},createjs.MouseEvent=createjs.promote(a,"Event")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c,d,e,f){this.setValues(a,b,c,d,e,f)}var b=a.prototype;a.DEG_TO_RAD=Math.PI/180,a.identity=null,b.setValues=function(a,b,c,d,e,f){return this.a=null==a?1:a,this.b=b||0,this.c=c||0,this.d=null==d?1:d,this.tx=e||0,this.ty=f||0,this},b.append=function(a,b,c,d,e,f){var g=this.a,h=this.b,i=this.c,j=this.d;return(1!=a||0!=b||0!=c||1!=d)&&(this.a=g*a+i*b,this.b=h*a+j*b,this.c=g*c+i*d,this.d=h*c+j*d),this.tx=g*e+i*f+this.tx,this.ty=h*e+j*f+this.ty,this},b.prepend=function(a,b,c,d,e,f){var g=this.a,h=this.c,i=this.tx;return this.a=a*g+c*this.b,this.b=b*g+d*this.b,this.c=a*h+c*this.d,this.d=b*h+d*this.d,this.tx=a*i+c*this.ty+e,this.ty=b*i+d*this.ty+f,this},b.appendMatrix=function(a){return this.append(a.a,a.b,a.c,a.d,a.tx,a.ty)},b.prependMatrix=function(a){return this.prepend(a.a,a.b,a.c,a.d,a.tx,a.ty)},b.appendTransform=function(b,c,d,e,f,g,h,i,j){if(f%360)var k=f*a.DEG_TO_RAD,l=Math.cos(k),m=Math.sin(k);else l=1,m=0;return g||h?(g*=a.DEG_TO_RAD,h*=a.DEG_TO_RAD,this.append(Math.cos(h),Math.sin(h),-Math.sin(g),Math.cos(g),b,c),this.append(l*d,m*d,-m*e,l*e,0,0)):this.append(l*d,m*d,-m*e,l*e,b,c),(i||j)&&(this.tx-=i*this.a+j*this.c,this.ty-=i*this.b+j*this.d),this},b.prependTransform=function(b,c,d,e,f,g,h,i,j){if(f%360)var k=f*a.DEG_TO_RAD,l=Math.cos(k),m=Math.sin(k);else l=1,m=0;return(i||j)&&(this.tx-=i,this.ty-=j),g||h?(g*=a.DEG_TO_RAD,h*=a.DEG_TO_RAD,this.prepend(l*d,m*d,-m*e,l*e,0,0),this.prepend(Math.cos(h),Math.sin(h),-Math.sin(g),Math.cos(g),b,c)):this.prepend(l*d,m*d,-m*e,l*e,b,c),this},b.rotate=function(b){b*=a.DEG_TO_RAD;var c=Math.cos(b),d=Math.sin(b),e=this.a,f=this.b;return this.a=e*c+this.c*d,this.b=f*c+this.d*d,this.c=-e*d+this.c*c,this.d=-f*d+this.d*c,this},b.skew=function(b,c){return b*=a.DEG_TO_RAD,c*=a.DEG_TO_RAD,this.append(Math.cos(c),Math.sin(c),-Math.sin(b),Math.cos(b),0,0),this},b.scale=function(a,b){return this.a*=a,this.b*=a,this.c*=b,this.d*=b,this},b.translate=function(a,b){return this.tx+=this.a*a+this.c*b,this.ty+=this.b*a+this.d*b,this},b.identity=function(){return this.a=this.d=1,this.b=this.c=this.tx=this.ty=0,this},b.invert=function(){var a=this.a,b=this.b,c=this.c,d=this.d,e=this.tx,f=a*d-b*c;return this.a=d/f,this.b=-b/f,this.c=-c/f,this.d=a/f,this.tx=(c*this.ty-d*e)/f,this.ty=-(a*this.ty-b*e)/f,this},b.isIdentity=function(){return 0===this.tx&&0===this.ty&&1===this.a&&0===this.b&&0===this.c&&1===this.d},b.equals=function(a){return this.tx===a.tx&&this.ty===a.ty&&this.a===a.a&&this.b===a.b&&this.c===a.c&&this.d===a.d},b.transformPoint=function(a,b,c){return c=c||{},c.x=a*this.a+b*this.c+this.tx,c.y=a*this.b+b*this.d+this.ty,c},b.decompose=function(b){null==b&&(b={}),b.x=this.tx,b.y=this.ty,b.scaleX=Math.sqrt(this.a*this.a+this.b*this.b),b.scaleY=Math.sqrt(this.c*this.c+this.d*this.d);var c=Math.atan2(-this.c,this.d),d=Math.atan2(this.b,this.a),e=Math.abs(1-c/d);return 1e-5>e?(b.rotation=d/a.DEG_TO_RAD,this.a<0&&this.d>=0&&(b.rotation+=b.rotation<=0?180:-180),b.skewX=b.skewY=0):(b.skewX=c/a.DEG_TO_RAD,b.skewY=d/a.DEG_TO_RAD),b},b.copy=function(a){return this.setValues(a.a,a.b,a.c,a.d,a.tx,a.ty)},b.clone=function(){return new a(this.a,this.b,this.c,this.d,this.tx,this.ty)},b.toString=function(){return"[Matrix2D (a="+this.a+" b="+this.b+" c="+this.c+" d="+this.d+" tx="+this.tx+" ty="+this.ty+")]"},a.identity=new a,createjs.Matrix2D=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c,d,e){this.setValues(a,b,c,d,e)}var b=a.prototype;b.setValues=function(a,b,c,d,e){return this.visible=null==a?!0:!!a,this.alpha=null==b?1:b,this.shadow=c,this.compositeOperation=d,this.matrix=e||this.matrix&&this.matrix.identity()||new createjs.Matrix2D,this},b.append=function(a,b,c,d,e){return this.alpha*=b,this.shadow=c||this.shadow,this.compositeOperation=d||this.compositeOperation,this.visible=this.visible&&a,e&&this.matrix.appendMatrix(e),this},b.prepend=function(a,b,c,d,e){return this.alpha*=b,this.shadow=this.shadow||c,this.compositeOperation=this.compositeOperation||d,this.visible=this.visible&&a,e&&this.matrix.prependMatrix(e),this},b.identity=function(){return this.visible=!0,this.alpha=1,this.shadow=this.compositeOperation=null,this.matrix.identity(),this},b.clone=function(){return new a(this.alpha,this.shadow,this.compositeOperation,this.visible,this.matrix.clone())},createjs.DisplayProps=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b){this.setValues(a,b)}var b=a.prototype;b.setValues=function(a,b){return this.x=a||0,this.y=b||0,this},b.copy=function(a){return this.x=a.x,this.y=a.y,this},b.clone=function(){return new a(this.x,this.y)},b.toString=function(){return"[Point (x="+this.x+" y="+this.y+")]"},createjs.Point=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c,d){this.setValues(a,b,c,d)}var b=a.prototype;b.setValues=function(a,b,c,d){return this.x=a||0,this.y=b||0,this.width=c||0,this.height=d||0,this},b.extend=function(a,b,c,d){return c=c||0,d=d||0,a+c>this.x+this.width&&(this.width=a+c-this.x),b+d>this.y+this.height&&(this.height=b+d-this.y),a=this.x&&a+c<=this.x+this.width&&b>=this.y&&b+d<=this.y+this.height},b.union=function(a){return this.clone().extend(a.x,a.y,a.width,a.height)},b.intersection=function(b){var c=b.x,d=b.y,e=c+b.width,f=d+b.height;return this.x>c&&(c=this.x),this.y>d&&(d=this.y),this.x+this.width=e||d>=f?null:new a(c,d,e-c,f-d)},b.intersects=function(a){return a.x<=this.x+this.width&&this.x<=a.x+a.width&&a.y<=this.y+this.height&&this.y<=a.y+a.height},b.isEmpty=function(){return this.width<=0||this.height<=0},b.clone=function(){return new a(this.x,this.y,this.width,this.height)},b.toString=function(){return"[Rectangle (x="+this.x+" y="+this.y+" width="+this.width+" height="+this.height+")]"},createjs.Rectangle=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c,d,e,f,g){a.addEventListener&&(this.target=a,this.overLabel=null==c?"over":c,this.outLabel=null==b?"out":b,this.downLabel=null==d?"down":d,this.play=e,this._isPressed=!1,this._isOver=!1,this._enabled=!1,a.mouseChildren=!1,this.enabled=!0,this.handleEvent({}),f&&(g&&(f.actionsEnabled=!1,f.gotoAndStop&&f.gotoAndStop(g)),a.hitArea=f))}var b=a.prototype;b.setEnabled=function(a){if(a!=this._enabled){var b=this.target;this._enabled=a,a?(b.cursor="pointer",b.addEventListener("rollover",this),b.addEventListener("rollout",this),b.addEventListener("mousedown",this),b.addEventListener("pressup",this),b._reset&&(b.__reset=b._reset,b._reset=this._reset)):(b.cursor=null,b.removeEventListener("rollover",this),b.removeEventListener("rollout",this),b.removeEventListener("mousedown",this),b.removeEventListener("pressup",this),b.__reset&&(b._reset=b.__reset,delete b.__reset))}},b.getEnabled=function(){return this._enabled};try{Object.defineProperties(b,{enabled:{get:b.getEnabled,set:b.setEnabled}})}catch(c){}b.toString=function(){return"[ButtonHelper]"},b.handleEvent=function(a){var b,c=this.target,d=a.type;"mousedown"==d?(this._isPressed=!0,b=this.downLabel):"pressup"==d?(this._isPressed=!1,b=this._isOver?this.overLabel:this.outLabel):"rollover"==d?(this._isOver=!0,b=this._isPressed?this.downLabel:this.overLabel):(this._isOver=!1,b=this._isPressed?this.overLabel:this.outLabel),this.play?c.gotoAndPlay&&c.gotoAndPlay(b):c.gotoAndStop&&c.gotoAndStop(b)},b._reset=function(){var a=this.paused;this.__reset(),this.paused=a},createjs.ButtonHelper=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c,d){this.color=a||"black",this.offsetX=b||0,this.offsetY=c||0,this.blur=d||0}var b=a.prototype;a.identity=new a("transparent",0,0,0),b.toString=function(){return"[Shadow]"},b.clone=function(){return new a(this.color,this.offsetX,this.offsetY,this.blur)},createjs.Shadow=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.EventDispatcher_constructor(),this.complete=!0,this.framerate=0,this._animations=null,this._frames=null,this._images=null,this._data=null,this._loadCount=0,this._frameHeight=0,this._frameWidth=0,this._numFrames=0,this._regX=0,this._regY=0,this._spacing=0,this._margin=0,this._parseData(a)}var b=createjs.extend(a,createjs.EventDispatcher);b.getAnimations=function(){return this._animations.slice()};try{Object.defineProperties(b,{animations:{get:b.getAnimations}})}catch(c){}b.getNumFrames=function(a){if(null==a)return this._frames?this._frames.length:this._numFrames||0;var b=this._data[a];return null==b?0:b.frames.length},b.getAnimation=function(a){return this._data[a]},b.getFrame=function(a){var b;return this._frames&&(b=this._frames[a])?b:null},b.getFrameBounds=function(a,b){var c=this.getFrame(a);return c?(b||new createjs.Rectangle).setValues(-c.regX,-c.regY,c.rect.width,c.rect.height):null},b.toString=function(){return"[SpriteSheet]"},b.clone=function(){throw"SpriteSheet cannot be cloned."},b._parseData=function(a){var b,c,d,e;if(null!=a){if(this.framerate=a.framerate||0,a.images&&(c=a.images.length)>0)for(e=this._images=[],b=0;c>b;b++){var f=a.images[b];if("string"==typeof f){var g=f;f=document.createElement("img"),f.src=g}e.push(f),f.getContext||f.naturalWidth||(this._loadCount++,this.complete=!1,function(a,b){f.onload=function(){a._handleImageLoad(b)}}(this,g),function(a,b){f.onerror=function(){a._handleImageError(b)}}(this,g))}if(null==a.frames);else if(Array.isArray(a.frames))for(this._frames=[],e=a.frames,b=0,c=e.length;c>b;b++){var h=e[b];this._frames.push({image:this._images[h[4]?h[4]:0],rect:new createjs.Rectangle(h[0],h[1],h[2],h[3]),regX:h[5]||0,regY:h[6]||0})}else d=a.frames,this._frameWidth=d.width,this._frameHeight=d.height,this._regX=d.regX||0,this._regY=d.regY||0,this._spacing=d.spacing||0,this._margin=d.margin||0,this._numFrames=d.count,0==this._loadCount&&this._calculateFrames();if(this._animations=[],null!=(d=a.animations)){this._data={};var i;for(i in d){var j={name:i},k=d[i];if("number"==typeof k)e=j.frames=[k];else if(Array.isArray(k))if(1==k.length)j.frames=[k[0]];else for(j.speed=k[3],j.next=k[2],e=j.frames=[],b=k[0];b<=k[1];b++)e.push(b);else{j.speed=k.speed,j.next=k.next;var l=k.frames;e=j.frames="number"==typeof l?[l]:l.slice(0)}(j.next===!0||void 0===j.next)&&(j.next=i),(j.next===!1||e.length<2&&j.next==i)&&(j.next=null),j.speed||(j.speed=1),this._animations.push(i),this._data[i]=j}}}},b._handleImageLoad=function(a){0==--this._loadCount&&(this._calculateFrames(),this.complete=!0,this.dispatchEvent("complete"))},b._handleImageError=function(a){var b=new createjs.Event("error");b.src=a,this.dispatchEvent(b),0==--this._loadCount&&this.dispatchEvent("complete")},b._calculateFrames=function(){if(!this._frames&&0!=this._frameWidth){this._frames=[];var a=this._numFrames||1e5,b=0,c=this._frameWidth,d=this._frameHeight,e=this._spacing,f=this._margin;a:for(var g=0,h=this._images;g=l;){for(var m=f;j-f-c>=m;){if(b>=a)break a;b++,this._frames.push({image:i,rect:new createjs.Rectangle(m,l,c,d),regX:this._regX,regY:this._regY}),m+=c+e}l+=d+e}this._numFrames=b}},createjs.SpriteSheet=createjs.promote(a,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function a(){this.command=null,this._stroke=null,this._strokeStyle=null,this._oldStrokeStyle=null,this._strokeDash=null,this._oldStrokeDash=null,this._strokeIgnoreScale=!1,this._fill=null,this._instructions=[],this._commitIndex=0,this._activeInstructions=[],this._dirty=!1,this._storeIndex=0,this.clear()}var b=a.prototype,c=a;a.getRGB=function(a,b,c,d){return null!=a&&null==c&&(d=b,c=255&a,b=a>>8&255,a=a>>16&255),null==d?"rgb("+a+","+b+","+c+")":"rgba("+a+","+b+","+c+","+d+")"},a.getHSL=function(a,b,c,d){return null==d?"hsl("+a%360+","+b+"%,"+c+"%)":"hsla("+a%360+","+b+"%,"+c+"%,"+d+")"},a.BASE_64={A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7,I:8,J:9,K:10,L:11,M:12,N:13,O:14,P:15,Q:16,R:17,S:18,T:19,U:20,V:21,W:22,X:23,Y:24,Z:25,a:26,b:27,c:28,d:29,e:30,f:31,g:32,h:33,i:34,j:35,k:36,l:37,m:38,n:39,o:40,p:41,q:42,r:43,s:44,t:45,u:46,v:47,w:48,x:49,y:50,z:51,0:52,1:53,2:54,3:55,4:56,5:57,6:58,7:59,8:60,9:61,"+":62,"/":63},a.STROKE_CAPS_MAP=["butt","round","square"],a.STROKE_JOINTS_MAP=["miter","round","bevel"];var d=createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");d.getContext&&(a._ctx=d.getContext("2d"),d.width=d.height=1),b.getInstructions=function(){return this._updateInstructions(),this._instructions};try{Object.defineProperties(b,{instructions:{get:b.getInstructions}})}catch(e){}b.isEmpty=function(){return!(this._instructions.length||this._activeInstructions.length)},b.draw=function(a,b){this._updateInstructions();for(var c=this._instructions,d=this._storeIndex,e=c.length;e>d;d++)c[d].exec(a,b)},b.drawAsPath=function(a){this._updateInstructions();for(var b,c=this._instructions,d=this._storeIndex,e=c.length;e>d;d++)(b=c[d]).path!==!1&&b.exec(a)},b.moveTo=function(a,b){return this.append(new c.MoveTo(a,b),!0)},b.lineTo=function(a,b){return this.append(new c.LineTo(a,b))},b.arcTo=function(a,b,d,e,f){return this.append(new c.ArcTo(a,b,d,e,f))},b.arc=function(a,b,d,e,f,g){return this.append(new c.Arc(a,b,d,e,f,g))},b.quadraticCurveTo=function(a,b,d,e){return this.append(new c.QuadraticCurveTo(a,b,d,e))},b.bezierCurveTo=function(a,b,d,e,f,g){return this.append(new c.BezierCurveTo(a,b,d,e,f,g))},b.rect=function(a,b,d,e){return this.append(new c.Rect(a,b,d,e))},b.closePath=function(){return this._activeInstructions.length?this.append(new c.ClosePath):this},b.clear=function(){return this._instructions.length=this._activeInstructions.length=this._commitIndex=0,this._strokeStyle=this._oldStrokeStyle=this._stroke=this._fill=this._strokeDash=this._oldStrokeDash=null,this._dirty=this._strokeIgnoreScale=!1,this},b.beginFill=function(a){return this._setFill(a?new c.Fill(a):null)},b.beginLinearGradientFill=function(a,b,d,e,f,g){return this._setFill((new c.Fill).linearGradient(a,b,d,e,f,g))},b.beginRadialGradientFill=function(a,b,d,e,f,g,h,i){return this._setFill((new c.Fill).radialGradient(a,b,d,e,f,g,h,i))},b.beginBitmapFill=function(a,b,d){return this._setFill(new c.Fill(null,d).bitmap(a,b))},b.endFill=function(){return this.beginFill()},b.setStrokeStyle=function(a,b,d,e,f){return this._updateInstructions(!0),this._strokeStyle=this.command=new c.StrokeStyle(a,b,d,e,f),this._stroke&&(this._stroke.ignoreScale=f),this._strokeIgnoreScale=f,this},b.setStrokeDash=function(a,b){return this._updateInstructions(!0),this._strokeDash=this.command=new c.StrokeDash(a,b),this},b.beginStroke=function(a){return this._setStroke(a?new c.Stroke(a):null)},b.beginLinearGradientStroke=function(a,b,d,e,f,g){return this._setStroke((new c.Stroke).linearGradient(a,b,d,e,f,g))},b.beginRadialGradientStroke=function(a,b,d,e,f,g,h,i){return this._setStroke((new c.Stroke).radialGradient(a,b,d,e,f,g,h,i))},b.beginBitmapStroke=function(a,b){return this._setStroke((new c.Stroke).bitmap(a,b))},b.endStroke=function(){return this.beginStroke()},b.curveTo=b.quadraticCurveTo,b.drawRect=b.rect,b.drawRoundRect=function(a,b,c,d,e){return this.drawRoundRectComplex(a,b,c,d,e,e,e,e)},b.drawRoundRectComplex=function(a,b,d,e,f,g,h,i){return this.append(new c.RoundRect(a,b,d,e,f,g,h,i))},b.drawCircle=function(a,b,d){return this.append(new c.Circle(a,b,d))},b.drawEllipse=function(a,b,d,e){return this.append(new c.Ellipse(a,b,d,e))},b.drawPolyStar=function(a,b,d,e,f,g){return this.append(new c.PolyStar(a,b,d,e,f,g))},b.append=function(a,b){return this._activeInstructions.push(a),this.command=a,b||(this._dirty=!0),this},b.decodePath=function(b){for(var c=[this.moveTo,this.lineTo,this.quadraticCurveTo,this.bezierCurveTo,this.closePath],d=[2,2,4,6,0],e=0,f=b.length,g=[],h=0,i=0,j=a.BASE_64;f>e;){var k=b.charAt(e),l=j[k],m=l>>3,n=c[m];if(!n||3&l)throw"bad path data (@"+e+"): "+k;var o=d[m];m||(h=i=0),g.length=0,e++;for(var p=(l>>2&1)+2,q=0;o>q;q++){var r=j[b.charAt(e)],s=r>>5?-1:1;r=(31&r)<<6|j[b.charAt(e+1)],3==p&&(r=r<<6|j[b.charAt(e+2)]),r=s*r/10,q%2?h=r+=h:i=r+=i,g[q]=r,e+=p}n.apply(this,g)}return this},b.store=function(){return this._updateInstructions(!0),this._storeIndex=this._instructions.length,this},b.unstore=function(){return this._storeIndex=0,this},b.clone=function(){var b=new a;return b.command=this.command,b._stroke=this._stroke,b._strokeStyle=this._strokeStyle,b._strokeDash=this._strokeDash,b._strokeIgnoreScale=this._strokeIgnoreScale,b._fill=this._fill,b._instructions=this._instructions.slice(),b._commitIndex=this._commitIndex,b._activeInstructions=this._activeInstructions.slice(),b._dirty=this._dirty,b._storeIndex=this._storeIndex,b},b.toString=function(){return"[Graphics]"},b.mt=b.moveTo,b.lt=b.lineTo,b.at=b.arcTo,b.bt=b.bezierCurveTo,b.qt=b.quadraticCurveTo,b.a=b.arc,b.r=b.rect,b.cp=b.closePath,b.c=b.clear,b.f=b.beginFill,b.lf=b.beginLinearGradientFill,b.rf=b.beginRadialGradientFill,b.bf=b.beginBitmapFill,b.ef=b.endFill,b.ss=b.setStrokeStyle,b.sd=b.setStrokeDash,b.s=b.beginStroke,b.ls=b.beginLinearGradientStroke,b.rs=b.beginRadialGradientStroke,b.bs=b.beginBitmapStroke,b.es=b.endStroke,b.dr=b.drawRect,b.rr=b.drawRoundRect,b.rc=b.drawRoundRectComplex,b.dc=b.drawCircle,b.de=b.drawEllipse,b.dp=b.drawPolyStar,b.p=b.decodePath,b._updateInstructions=function(b){var c=this._instructions,d=this._activeInstructions,e=this._commitIndex;if(this._dirty&&d.length){c.length=e,c.push(a.beginCmd);var f=d.length,g=c.length;c.length=g+f;for(var h=0;f>h;h++)c[h+g]=d[h];this._fill&&c.push(this._fill),this._stroke&&(this._strokeDash!==this._oldStrokeDash&&(this._oldStrokeDash=this._strokeDash,c.push(this._strokeDash)),this._strokeStyle!==this._oldStrokeStyle&&(this._oldStrokeStyle=this._strokeStyle,c.push(this._strokeStyle)),c.push(this._stroke)),this._dirty=!1}b&&(d.length=0,this._commitIndex=c.length)},b._setFill=function(a){return this._updateInstructions(!0),this.command=this._fill=a,this},b._setStroke=function(a){return this._updateInstructions(!0),(this.command=this._stroke=a)&&(a.ignoreScale=this._strokeIgnoreScale),this},(c.LineTo=function(a,b){this.x=a,this.y=b}).prototype.exec=function(a){a.lineTo(this.x,this.y)},(c.MoveTo=function(a,b){this.x=a,this.y=b}).prototype.exec=function(a){a.moveTo(this.x,this.y)},(c.ArcTo=function(a,b,c,d,e){this.x1=a,this.y1=b,this.x2=c,this.y2=d,this.radius=e}).prototype.exec=function(a){a.arcTo(this.x1,this.y1,this.x2,this.y2,this.radius)},(c.Arc=function(a,b,c,d,e,f){this.x=a,this.y=b,this.radius=c,this.startAngle=d,this.endAngle=e,this.anticlockwise=!!f}).prototype.exec=function(a){a.arc(this.x,this.y,this.radius,this.startAngle,this.endAngle,this.anticlockwise)},(c.QuadraticCurveTo=function(a,b,c,d){this.cpx=a,this.cpy=b,this.x=c,this.y=d}).prototype.exec=function(a){a.quadraticCurveTo(this.cpx,this.cpy,this.x,this.y)},(c.BezierCurveTo=function(a,b,c,d,e,f){this.cp1x=a,this.cp1y=b,this.cp2x=c,this.cp2y=d,this.x=e,this.y=f}).prototype.exec=function(a){a.bezierCurveTo(this.cp1x,this.cp1y,this.cp2x,this.cp2y,this.x,this.y)},(c.Rect=function(a,b,c,d){this.x=a,this.y=b,this.w=c,this.h=d}).prototype.exec=function(a){a.rect(this.x,this.y,this.w,this.h)},(c.ClosePath=function(){}).prototype.exec=function(a){a.closePath()},(c.BeginPath=function(){}).prototype.exec=function(a){a.beginPath()},b=(c.Fill=function(a,b){this.style=a,this.matrix=b}).prototype,b.exec=function(a){if(this.style){a.fillStyle=this.style;var b=this.matrix;b&&(a.save(),a.transform(b.a,b.b,b.c,b.d,b.tx,b.ty)),a.fill(),b&&a.restore()}},b.linearGradient=function(b,c,d,e,f,g){for(var h=this.style=a._ctx.createLinearGradient(d,e,f,g),i=0,j=b.length;j>i;i++)h.addColorStop(c[i],b[i]);return h.props={colors:b,ratios:c,x0:d,y0:e,x1:f,y1:g,type:"linear"},this},b.radialGradient=function(b,c,d,e,f,g,h,i){for(var j=this.style=a._ctx.createRadialGradient(d,e,f,g,h,i),k=0,l=b.length;l>k;k++)j.addColorStop(c[k],b[k]);return j.props={colors:b,ratios:c,x0:d,y0:e,r0:f,x1:g,y1:h,r1:i,type:"radial"},this},b.bitmap=function(b,c){if(b.naturalWidth||b.getContext||b.readyState>=2){var d=this.style=a._ctx.createPattern(b,c||"");d.props={image:b,repetition:c,type:"bitmap"}}return this},b.path=!1,b=(c.Stroke=function(a,b){this.style=a,this.ignoreScale=b}).prototype,b.exec=function(a){this.style&&(a.strokeStyle=this.style,this.ignoreScale&&(a.save(),a.setTransform(1,0,0,1,0,0)),a.stroke(),this.ignoreScale&&a.restore())},b.linearGradient=c.Fill.prototype.linearGradient,b.radialGradient=c.Fill.prototype.radialGradient,b.bitmap=c.Fill.prototype.bitmap,b.path=!1,b=(c.StrokeStyle=function(a,b,c,d,e){this.width=a,this.caps=b,this.joints=c,this.miterLimit=d,this.ignoreScale=e}).prototype,b.exec=function(b){b.lineWidth=null==this.width?"1":this.width,b.lineCap=null==this.caps?"butt":isNaN(this.caps)?this.caps:a.STROKE_CAPS_MAP[this.caps],b.lineJoin=null==this.joints?"miter":isNaN(this.joints)?this.joints:a.STROKE_JOINTS_MAP[this.joints],b.miterLimit=null==this.miterLimit?"10":this.miterLimit,b.ignoreScale=null==this.ignoreScale?!1:this.ignoreScale},b.path=!1,(c.StrokeDash=function(a,b){this.segments=a,this.offset=b||0}).prototype.exec=function(a){a.setLineDash&&(a.setLineDash(this.segments||c.StrokeDash.EMPTY_SEGMENTS),a.lineDashOffset=this.offset||0)},c.StrokeDash.EMPTY_SEGMENTS=[],(c.RoundRect=function(a,b,c,d,e,f,g,h){this.x=a,this.y=b,this.w=c,this.h=d,this.radiusTL=e,this.radiusTR=f,this.radiusBR=g,this.radiusBL=h}).prototype.exec=function(a){var b=(j>i?i:j)/2,c=0,d=0,e=0,f=0,g=this.x,h=this.y,i=this.w,j=this.h,k=this.radiusTL,l=this.radiusTR,m=this.radiusBR,n=this.radiusBL;0>k&&(k*=c=-1),k>b&&(k=b),0>l&&(l*=d=-1),l>b&&(l=b),0>m&&(m*=e=-1),m>b&&(m=b),0>n&&(n*=f=-1),n>b&&(n=b),a.moveTo(g+i-l,h),a.arcTo(g+i+l*d,h-l*d,g+i,h+l,l),a.lineTo(g+i,h+j-m),a.arcTo(g+i+m*e,h+j+m*e,g+i-m,h+j,m),a.lineTo(g+n,h+j),a.arcTo(g-n*f,h+j+n*f,g,h+j-n,n),a.lineTo(g,h+k),a.arcTo(g-k*c,h-k*c,g+k,h,k),a.closePath()},(c.Circle=function(a,b,c){this.x=a,this.y=b,this.radius=c}).prototype.exec=function(a){a.arc(this.x,this.y,this.radius,0,2*Math.PI)},(c.Ellipse=function(a,b,c,d){this.x=a,this.y=b,this.w=c,this.h=d}).prototype.exec=function(a){var b=this.x,c=this.y,d=this.w,e=this.h,f=.5522848,g=d/2*f,h=e/2*f,i=b+d,j=c+e,k=b+d/2,l=c+e/2;a.moveTo(b,l),a.bezierCurveTo(b,l-h,k-g,c,k,c),a.bezierCurveTo(k+g,c,i,l-h,i,l),a.bezierCurveTo(i,l+h,k+g,j,k,j),a.bezierCurveTo(k-g,j,b,l+h,b,l)},(c.PolyStar=function(a,b,c,d,e,f){this.x=a,this.y=b,this.radius=c,this.sides=d,this.pointSize=e,this.angle=f}).prototype.exec=function(a){var b=this.x,c=this.y,d=this.radius,e=(this.angle||0)/180*Math.PI,f=this.sides,g=1-(this.pointSize||0),h=Math.PI/f;a.moveTo(b+Math.cos(e)*d,c+Math.sin(e)*d);for(var i=0;f>i;i++)e+=h,1!=g&&a.lineTo(b+Math.cos(e)*d*g,c+Math.sin(e)*d*g),e+=h,a.lineTo(b+Math.cos(e)*d,c+Math.sin(e)*d);a.closePath()},a.beginCmd=new c.BeginPath,createjs.Graphics=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(){this.EventDispatcher_constructor(),this.alpha=1,this.cacheCanvas=null,this.cacheID=0,this.id=createjs.UID.get(),this.mouseEnabled=!0,this.tickEnabled=!0,this.name=null,this.parent=null,this.regX=0,this.regY=0,this.rotation=0,this.scaleX=1,this.scaleY=1,this.skewX=0,this.skewY=0,this.shadow=null,this.visible=!0,this.x=0,this.y=0,this.transformMatrix=null,this.compositeOperation=null,this.snapToPixel=!0,this.filters=null, +this.mask=null,this.hitArea=null,this.cursor=null,this._cacheOffsetX=0,this._cacheOffsetY=0,this._filterOffsetX=0,this._filterOffsetY=0,this._cacheScale=1,this._cacheDataURLID=0,this._cacheDataURL=null,this._props=new createjs.DisplayProps,this._rectangle=new createjs.Rectangle,this._bounds=null}var b=createjs.extend(a,createjs.EventDispatcher);a._MOUSE_EVENTS=["click","dblclick","mousedown","mouseout","mouseover","pressmove","pressup","rollout","rollover"],a.suppressCrossDomainErrors=!1,a._snapToPixelEnabled=!1;var c=createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");c.getContext&&(a._hitTestCanvas=c,a._hitTestContext=c.getContext("2d"),c.width=c.height=1),a._nextCacheID=1,b.getStage=function(){for(var a=this,b=createjs.Stage;a.parent;)a=a.parent;return a instanceof b?a:null};try{Object.defineProperties(b,{stage:{get:b.getStage}})}catch(d){}b.isVisible=function(){return!!(this.visible&&this.alpha>0&&0!=this.scaleX&&0!=this.scaleY)},b.draw=function(a,b){var c=this.cacheCanvas;if(b||!c)return!1;var d=this._cacheScale;return a.drawImage(c,this._cacheOffsetX+this._filterOffsetX,this._cacheOffsetY+this._filterOffsetY,c.width/d,c.height/d),!0},b.updateContext=function(b){var c=this,d=c.mask,e=c._props.matrix;d&&d.graphics&&!d.graphics.isEmpty()&&(d.getMatrix(e),b.transform(e.a,e.b,e.c,e.d,e.tx,e.ty),d.graphics.drawAsPath(b),b.clip(),e.invert(),b.transform(e.a,e.b,e.c,e.d,e.tx,e.ty)),this.getMatrix(e);var f=e.tx,g=e.ty;a._snapToPixelEnabled&&c.snapToPixel&&(f=f+(0>f?-.5:.5)|0,g=g+(0>g?-.5:.5)|0),b.transform(e.a,e.b,e.c,e.d,f,g),b.globalAlpha*=c.alpha,c.compositeOperation&&(b.globalCompositeOperation=c.compositeOperation),c.shadow&&this._applyShadow(b,c.shadow)},b.cache=function(a,b,c,d,e){e=e||1,this.cacheCanvas||(this.cacheCanvas=createjs.createCanvas?createjs.createCanvas():document.createElement("canvas")),this._cacheWidth=c,this._cacheHeight=d,this._cacheOffsetX=a,this._cacheOffsetY=b,this._cacheScale=e,this.updateCache()},b.updateCache=function(b){var c=this.cacheCanvas;if(!c)throw"cache() must be called before updateCache()";var d=this._cacheScale,e=this._cacheOffsetX*d,f=this._cacheOffsetY*d,g=this._cacheWidth,h=this._cacheHeight,i=c.getContext("2d"),j=this._getFilterBounds();e+=this._filterOffsetX=j.x,f+=this._filterOffsetY=j.y,g=Math.ceil(g*d)+j.width,h=Math.ceil(h*d)+j.height,g!=c.width||h!=c.height?(c.width=g,c.height=h):b||i.clearRect(0,0,g+1,h+1),i.save(),i.globalCompositeOperation=b,i.setTransform(d,0,0,d,-e,-f),this.draw(i,!0),this._applyFilters(),i.restore(),this.cacheID=a._nextCacheID++},b.uncache=function(){this._cacheDataURL=this.cacheCanvas=null,this.cacheID=this._cacheOffsetX=this._cacheOffsetY=this._filterOffsetX=this._filterOffsetY=0,this._cacheScale=1},b.getCacheDataURL=function(){return this.cacheCanvas?(this.cacheID!=this._cacheDataURLID&&(this._cacheDataURL=this.cacheCanvas.toDataURL()),this._cacheDataURL):null},b.localToGlobal=function(a,b,c){return this.getConcatenatedMatrix(this._props.matrix).transformPoint(a,b,c||new createjs.Point)},b.globalToLocal=function(a,b,c){return this.getConcatenatedMatrix(this._props.matrix).invert().transformPoint(a,b,c||new createjs.Point)},b.localToLocal=function(a,b,c,d){return d=this.localToGlobal(a,b,d),c.globalToLocal(d.x,d.y,d)},b.setTransform=function(a,b,c,d,e,f,g,h,i){return this.x=a||0,this.y=b||0,this.scaleX=null==c?1:c,this.scaleY=null==d?1:d,this.rotation=e||0,this.skewX=f||0,this.skewY=g||0,this.regX=h||0,this.regY=i||0,this},b.getMatrix=function(a){var b=this,c=a&&a.identity()||new createjs.Matrix2D;return b.transformMatrix?c.copy(b.transformMatrix):c.appendTransform(b.x,b.y,b.scaleX,b.scaleY,b.rotation,b.skewX,b.skewY,b.regX,b.regY)},b.getConcatenatedMatrix=function(a){for(var b=this,c=this.getMatrix(a);b=b.parent;)c.prependMatrix(b.getMatrix(b._props.matrix));return c},b.getConcatenatedDisplayProps=function(a){a=a?a.identity():new createjs.DisplayProps;var b=this,c=b.getMatrix(a.matrix);do a.prepend(b.visible,b.alpha,b.shadow,b.compositeOperation),b!=this&&c.prependMatrix(b.getMatrix(b._props.matrix));while(b=b.parent);return a},b.hitTest=function(b,c){var d=a._hitTestContext;d.setTransform(1,0,0,1,-b,-c),this.draw(d);var e=this._testHit(d);return d.setTransform(1,0,0,1,0,0),d.clearRect(0,0,2,2),e},b.set=function(a){for(var b in a)this[b]=a[b];return this},b.getBounds=function(){if(this._bounds)return this._rectangle.copy(this._bounds);var a=this.cacheCanvas;if(a){var b=this._cacheScale;return this._rectangle.setValues(this._cacheOffsetX,this._cacheOffsetY,a.width/b,a.height/b)}return null},b.getTransformedBounds=function(){return this._getBounds()},b.setBounds=function(a,b,c,d){null==a&&(this._bounds=a),this._bounds=(this._bounds||new createjs.Rectangle).setValues(a,b,c,d)},b.clone=function(){return this._cloneProps(new a)},b.toString=function(){return"[DisplayObject (name="+this.name+")]"},b._cloneProps=function(a){return a.alpha=this.alpha,a.mouseEnabled=this.mouseEnabled,a.tickEnabled=this.tickEnabled,a.name=this.name,a.regX=this.regX,a.regY=this.regY,a.rotation=this.rotation,a.scaleX=this.scaleX,a.scaleY=this.scaleY,a.shadow=this.shadow,a.skewX=this.skewX,a.skewY=this.skewY,a.visible=this.visible,a.x=this.x,a.y=this.y,a.compositeOperation=this.compositeOperation,a.snapToPixel=this.snapToPixel,a.filters=null==this.filters?null:this.filters.slice(0),a.mask=this.mask,a.hitArea=this.hitArea,a.cursor=this.cursor,a._bounds=this._bounds,a},b._applyShadow=function(a,b){b=b||Shadow.identity,a.shadowColor=b.color,a.shadowOffsetX=b.offsetX,a.shadowOffsetY=b.offsetY,a.shadowBlur=b.blur},b._tick=function(a){var b=this._listeners;b&&b.tick&&(a.target=null,a.propagationStopped=a.immediatePropagationStopped=!1,this.dispatchEvent(a))},b._testHit=function(b){try{var c=b.getImageData(0,0,1,1).data[3]>1}catch(d){if(!a.suppressCrossDomainErrors)throw"An error has occurred. This is most likely due to security restrictions on reading canvas pixel data with local or cross-domain images."}return c},b._applyFilters=function(){if(this.filters&&0!=this.filters.length&&this.cacheCanvas)for(var a=this.filters.length,b=this.cacheCanvas.getContext("2d"),c=this.cacheCanvas.width,d=this.cacheCanvas.height,e=0;a>e;e++)this.filters[e].applyFilter(b,0,0,c,d)},b._getFilterBounds=function(a){var b,c=this.filters,d=this._rectangle.setValues(0,0,0,0);if(!c||!(b=c.length))return d;for(var e=0;b>e;e++){var f=this.filters[e];f.getBounds&&f.getBounds(d)}return d},b._getBounds=function(a,b){return this._transformBounds(this.getBounds(),a,b)},b._transformBounds=function(a,b,c){if(!a)return a;var d=a.x,e=a.y,f=a.width,g=a.height,h=this._props.matrix;h=c?h.identity():this.getMatrix(h),(d||e)&&h.appendTransform(0,0,1,1,0,0,0,-d,-e),b&&h.prependMatrix(b);var i=f*h.a,j=f*h.b,k=g*h.c,l=g*h.d,m=h.tx,n=h.ty,o=m,p=m,q=n,r=n;return(d=i+m)p&&(p=d),(d=i+k+m)p&&(p=d),(d=k+m)p&&(p=d),(e=j+n)r&&(r=e),(e=j+l+n)r&&(r=e),(e=l+n)r&&(r=e),a.setValues(o,q,p-o,r-q)},b._hasMouseEventListener=function(){for(var b=a._MOUSE_EVENTS,c=0,d=b.length;d>c;c++)if(this.hasEventListener(b[c]))return!0;return!!this.cursor},createjs.DisplayObject=createjs.promote(a,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function a(){this.DisplayObject_constructor(),this.children=[],this.mouseChildren=!0,this.tickChildren=!0}var b=createjs.extend(a,createjs.DisplayObject);b.getNumChildren=function(){return this.children.length};try{Object.defineProperties(b,{numChildren:{get:b.getNumChildren}})}catch(c){}b.initialize=a,b.isVisible=function(){var a=this.cacheCanvas||this.children.length;return!!(this.visible&&this.alpha>0&&0!=this.scaleX&&0!=this.scaleY&&a)},b.draw=function(a,b){if(this.DisplayObject_draw(a,b))return!0;for(var c=this.children.slice(),d=0,e=c.length;e>d;d++){var f=c[d];f.isVisible()&&(a.save(),f.updateContext(a),f.draw(a),a.restore())}return!0},b.addChild=function(a){if(null==a)return a;var b=arguments.length;if(b>1){for(var c=0;b>c;c++)this.addChild(arguments[c]);return arguments[b-1]}return a.parent&&a.parent.removeChild(a),a.parent=this,this.children.push(a),a.dispatchEvent("added"),a},b.addChildAt=function(a,b){var c=arguments.length,d=arguments[c-1];if(0>d||d>this.children.length)return arguments[c-2];if(c>2){for(var e=0;c-1>e;e++)this.addChildAt(arguments[e],d+e);return arguments[c-2]}return a.parent&&a.parent.removeChild(a),a.parent=this,this.children.splice(b,0,a),a.dispatchEvent("added"),a},b.removeChild=function(a){var b=arguments.length;if(b>1){for(var c=!0,d=0;b>d;d++)c=c&&this.removeChild(arguments[d]);return c}return this.removeChildAt(createjs.indexOf(this.children,a))},b.removeChildAt=function(a){var b=arguments.length;if(b>1){for(var c=[],d=0;b>d;d++)c[d]=arguments[d];c.sort(function(a,b){return b-a});for(var e=!0,d=0;b>d;d++)e=e&&this.removeChildAt(c[d]);return e}if(0>a||a>this.children.length-1)return!1;var f=this.children[a];return f&&(f.parent=null),this.children.splice(a,1),f.dispatchEvent("removed"),!0},b.removeAllChildren=function(){for(var a=this.children;a.length;)this.removeChildAt(0)},b.getChildAt=function(a){return this.children[a]},b.getChildByName=function(a){for(var b=this.children,c=0,d=b.length;d>c;c++)if(b[c].name==a)return b[c];return null},b.sortChildren=function(a){this.children.sort(a)},b.getChildIndex=function(a){return createjs.indexOf(this.children,a)},b.swapChildrenAt=function(a,b){var c=this.children,d=c[a],e=c[b];d&&e&&(c[a]=e,c[b]=d)},b.swapChildren=function(a,b){for(var c,d,e=this.children,f=0,g=e.length;g>f&&(e[f]==a&&(c=f),e[f]==b&&(d=f),null==c||null==d);f++);f!=g&&(e[c]=b,e[d]=a)},b.setChildIndex=function(a,b){var c=this.children,d=c.length;if(!(a.parent!=this||0>b||b>=d)){for(var e=0;d>e&&c[e]!=a;e++);e!=d&&e!=b&&(c.splice(e,1),c.splice(b,0,a))}},b.contains=function(a){for(;a;){if(a==this)return!0;a=a.parent}return!1},b.hitTest=function(a,b){return null!=this.getObjectUnderPoint(a,b)},b.getObjectsUnderPoint=function(a,b,c){var d=[],e=this.localToGlobal(a,b);return this._getObjectsUnderPoint(e.x,e.y,d,c>0,1==c),d},b.getObjectUnderPoint=function(a,b,c){var d=this.localToGlobal(a,b);return this._getObjectsUnderPoint(d.x,d.y,null,c>0,1==c)},b.getBounds=function(){return this._getBounds(null,!0)},b.getTransformedBounds=function(){return this._getBounds()},b.clone=function(b){var c=this._cloneProps(new a);return b&&this._cloneChildren(c),c},b.toString=function(){return"[Container (name="+this.name+")]"},b._tick=function(a){if(this.tickChildren)for(var b=this.children.length-1;b>=0;b--){var c=this.children[b];c.tickEnabled&&c._tick&&c._tick(a)}this.DisplayObject__tick(a)},b._cloneChildren=function(a){a.children.length&&a.removeAllChildren();for(var b=a.children,c=0,d=this.children.length;d>c;c++){var e=this.children[c].clone(!0);e.parent=a,b.push(e)}},b._getObjectsUnderPoint=function(b,c,d,e,f,g){if(g=g||0,!g&&!this._testMask(this,b,c))return null;var h,i=createjs.DisplayObject._hitTestContext;f=f||e&&this._hasMouseEventListener();for(var j=this.children,k=j.length,l=k-1;l>=0;l--){var m=j[l],n=m.hitArea;if(m.visible&&(n||m.isVisible())&&(!e||m.mouseEnabled)&&(n||this._testMask(m,b,c)))if(!n&&m instanceof a){var o=m._getObjectsUnderPoint(b,c,d,e,f,g+1);if(!d&&o)return e&&!this.mouseChildren?this:o}else{if(e&&!f&&!m._hasMouseEventListener())continue;var p=m.getConcatenatedDisplayProps(m._props);if(h=p.matrix,n&&(h.appendMatrix(n.getMatrix(n._props.matrix)),p.alpha=n.alpha),i.globalAlpha=p.alpha,i.setTransform(h.a,h.b,h.c,h.d,h.tx-b,h.ty-c),(n||m).draw(i),!this._testHit(i))continue;if(i.setTransform(1,0,0,1,0,0),i.clearRect(0,0,2,2),!d)return e&&!this.mouseChildren?this:m;d.push(m)}}return null},b._testMask=function(a,b,c){var d=a.mask;if(!d||!d.graphics||d.graphics.isEmpty())return!0;var e=this._props.matrix,f=a.parent;e=f?f.getConcatenatedMatrix(e):e.identity(),e=d.getMatrix(d._props.matrix).prependMatrix(e);var g=createjs.DisplayObject._hitTestContext;return g.setTransform(e.a,e.b,e.c,e.d,e.tx-b,e.ty-c),d.graphics.drawAsPath(g),g.fillStyle="#000",g.fill(),this._testHit(g)?(g.setTransform(1,0,0,1,0,0),g.clearRect(0,0,2,2),!0):!1},b._getBounds=function(a,b){var c=this.DisplayObject_getBounds();if(c)return this._transformBounds(c,a,b);var d=this._props.matrix;d=b?d.identity():this.getMatrix(d),a&&d.prependMatrix(a);for(var e=this.children.length,f=null,g=0;e>g;g++){var h=this.children[g];h.visible&&(c=h._getBounds(d))&&(f?f.extend(c.x,c.y,c.width,c.height):f=c.clone())}return f},createjs.Container=createjs.promote(a,"DisplayObject")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.Container_constructor(),this.autoClear=!0,this.canvas="string"==typeof a?document.getElementById(a):a,this.mouseX=0,this.mouseY=0,this.drawRect=null,this.snapToPixelEnabled=!1,this.mouseInBounds=!1,this.tickOnUpdate=!0,this.mouseMoveOutside=!1,this.preventSelection=!0,this._pointerData={},this._pointerCount=0,this._primaryPointerID=null,this._mouseOverIntervalID=null,this._nextStage=null,this._prevStage=null,this.enableDOMEvents(!0)}var b=createjs.extend(a,createjs.Container);b._get_nextStage=function(){return this._nextStage},b._set_nextStage=function(a){this._nextStage&&(this._nextStage._prevStage=null),a&&(a._prevStage=this),this._nextStage=a};try{Object.defineProperties(b,{nextStage:{get:b._get_nextStage,set:b._set_nextStage}})}catch(c){}b.update=function(a){if(this.canvas&&(this.tickOnUpdate&&this.tick(a),this.dispatchEvent("drawstart",!1,!0)!==!1)){createjs.DisplayObject._snapToPixelEnabled=this.snapToPixelEnabled;var b=this.drawRect,c=this.canvas.getContext("2d");c.setTransform(1,0,0,1,0,0),this.autoClear&&(b?c.clearRect(b.x,b.y,b.width,b.height):c.clearRect(0,0,this.canvas.width+1,this.canvas.height+1)),c.save(),this.drawRect&&(c.beginPath(),c.rect(b.x,b.y,b.width,b.height),c.clip()),this.updateContext(c),this.draw(c,!1),c.restore(),this.dispatchEvent("drawend")}},b.tick=function(a){if(this.tickEnabled&&this.dispatchEvent("tickstart",!1,!0)!==!1){var b=new createjs.Event("tick");if(a)for(var c in a)a.hasOwnProperty(c)&&(b[c]=a[c]);this._tick(b),this.dispatchEvent("tickend")}},b.handleEvent=function(a){"tick"==a.type&&this.update(a)},b.clear=function(){if(this.canvas){var a=this.canvas.getContext("2d");a.setTransform(1,0,0,1,0,0),a.clearRect(0,0,this.canvas.width+1,this.canvas.height+1)}},b.toDataURL=function(a,b){var c,d=this.canvas.getContext("2d"),e=this.canvas.width,f=this.canvas.height;if(a){c=d.getImageData(0,0,e,f);var g=d.globalCompositeOperation;d.globalCompositeOperation="destination-over",d.fillStyle=a,d.fillRect(0,0,e,f)}var h=this.canvas.toDataURL(b||"image/png");return a&&(d.putImageData(c,0,0),d.globalCompositeOperation=g),h},b.enableMouseOver=function(a){if(this._mouseOverIntervalID&&(clearInterval(this._mouseOverIntervalID),this._mouseOverIntervalID=null,0==a&&this._testMouseOver(!0)),null==a)a=20;else if(0>=a)return;var b=this;this._mouseOverIntervalID=setInterval(function(){b._testMouseOver()},1e3/Math.min(50,a))},b.enableDOMEvents=function(a){null==a&&(a=!0);var b,c,d=this._eventListeners;if(!a&&d){for(b in d)c=d[b],c.t.removeEventListener(b,c.f,!1);this._eventListeners=null}else if(a&&!d&&this.canvas){var e=window.addEventListener?window:document,f=this;d=this._eventListeners={},d.mouseup={t:e,f:function(a){f._handleMouseUp(a)}},d.mousemove={t:e,f:function(a){f._handleMouseMove(a)}},d.dblclick={t:this.canvas,f:function(a){f._handleDoubleClick(a)}},d.mousedown={t:this.canvas,f:function(a){f._handleMouseDown(a)}};for(b in d)c=d[b],c.t.addEventListener(b,c.f,!1)}},b.clone=function(){throw"Stage cannot be cloned."},b.toString=function(){return"[Stage (name="+this.name+")]"},b._getElementRect=function(a){var b;try{b=a.getBoundingClientRect()}catch(c){b={top:a.offsetTop,left:a.offsetLeft,width:a.offsetWidth,height:a.offsetHeight}}var d=(window.pageXOffset||document.scrollLeft||0)-(document.clientLeft||document.body.clientLeft||0),e=(window.pageYOffset||document.scrollTop||0)-(document.clientTop||document.body.clientTop||0),f=window.getComputedStyle?getComputedStyle(a,null):a.currentStyle,g=parseInt(f.paddingLeft)+parseInt(f.borderLeftWidth),h=parseInt(f.paddingTop)+parseInt(f.borderTopWidth),i=parseInt(f.paddingRight)+parseInt(f.borderRightWidth),j=parseInt(f.paddingBottom)+parseInt(f.borderBottomWidth);return{left:b.left+d+g,right:b.right+d-i,top:b.top+e+h,bottom:b.bottom+e-j}},b._getPointerData=function(a){var b=this._pointerData[a];return b||(b=this._pointerData[a]={x:0,y:0}),b},b._handleMouseMove=function(a){a||(a=window.event),this._handlePointerMove(-1,a,a.pageX,a.pageY)},b._handlePointerMove=function(a,b,c,d,e){if((!this._prevStage||void 0!==e)&&this.canvas){var f=this._nextStage,g=this._getPointerData(a),h=g.inBounds;this._updatePointerPosition(a,b,c,d),(h||g.inBounds||this.mouseMoveOutside)&&(-1===a&&g.inBounds==!h&&this._dispatchMouseEvent(this,h?"mouseleave":"mouseenter",!1,a,g,b),this._dispatchMouseEvent(this,"stagemousemove",!1,a,g,b),this._dispatchMouseEvent(g.target,"pressmove",!0,a,g,b)),f&&f._handlePointerMove(a,b,c,d,null)}},b._updatePointerPosition=function(a,b,c,d){var e=this._getElementRect(this.canvas);c-=e.left,d-=e.top;var f=this.canvas.width,g=this.canvas.height;c/=(e.right-e.left)/f,d/=(e.bottom-e.top)/g;var h=this._getPointerData(a);(h.inBounds=c>=0&&d>=0&&f-1>=c&&g-1>=d)?(h.x=c,h.y=d):this.mouseMoveOutside&&(h.x=0>c?0:c>f-1?f-1:c,h.y=0>d?0:d>g-1?g-1:d),h.posEvtObj=b,h.rawX=c,h.rawY=d,(a===this._primaryPointerID||-1===a)&&(this.mouseX=h.x,this.mouseY=h.y,this.mouseInBounds=h.inBounds)},b._handleMouseUp=function(a){this._handlePointerUp(-1,a,!1)},b._handlePointerUp=function(a,b,c,d){var e=this._nextStage,f=this._getPointerData(a);if(!this._prevStage||void 0!==d){var g=null,h=f.target;d||!h&&!e||(g=this._getObjectsUnderPoint(f.x,f.y,null,!0)),f.down&&(this._dispatchMouseEvent(this,"stagemouseup",!1,a,f,b,g),f.down=!1),g==h&&this._dispatchMouseEvent(h,"click",!0,a,f,b),this._dispatchMouseEvent(h,"pressup",!0,a,f,b),c?(a==this._primaryPointerID&&(this._primaryPointerID=null),delete this._pointerData[a]):f.target=null,e&&e._handlePointerUp(a,b,c,d||g&&this)}},b._handleMouseDown=function(a){this._handlePointerDown(-1,a,a.pageX,a.pageY)},b._handlePointerDown=function(a,b,c,d,e){this.preventSelection&&b.preventDefault(),(null==this._primaryPointerID||-1===a)&&(this._primaryPointerID=a),null!=d&&this._updatePointerPosition(a,b,c,d);var f=null,g=this._nextStage,h=this._getPointerData(a);e||(f=h.target=this._getObjectsUnderPoint(h.x,h.y,null,!0)),h.inBounds&&(this._dispatchMouseEvent(this,"stagemousedown",!1,a,h,b,f),h.down=!0),this._dispatchMouseEvent(f,"mousedown",!0,a,h,b),g&&g._handlePointerDown(a,b,c,d,e||f&&this)},b._testMouseOver=function(a,b,c){if(!this._prevStage||void 0!==b){var d=this._nextStage;if(!this._mouseOverIntervalID)return void(d&&d._testMouseOver(a,b,c));var e=this._getPointerData(-1);if(e&&(a||this.mouseX!=this._mouseOverX||this.mouseY!=this._mouseOverY||!this.mouseInBounds)){var f,g,h,i=e.posEvtObj,j=c||i&&i.target==this.canvas,k=null,l=-1,m="";!b&&(a||this.mouseInBounds&&j)&&(k=this._getObjectsUnderPoint(this.mouseX,this.mouseY,null,!0),this._mouseOverX=this.mouseX,this._mouseOverY=this.mouseY);var n=this._mouseOverTarget||[],o=n[n.length-1],p=this._mouseOverTarget=[];for(f=k;f;)p.unshift(f),m||(m=f.cursor),f=f.parent;for(this.canvas.style.cursor=m,!b&&c&&(c.canvas.style.cursor=m),g=0,h=p.length;h>g&&p[g]==n[g];g++)l=g;for(o!=k&&this._dispatchMouseEvent(o,"mouseout",!0,-1,e,i,k),g=n.length-1;g>l;g--)this._dispatchMouseEvent(n[g],"rollout",!1,-1,e,i,k);for(g=p.length-1;g>l;g--)this._dispatchMouseEvent(p[g],"rollover",!1,-1,e,i,o);o!=k&&this._dispatchMouseEvent(k,"mouseover",!0,-1,e,i,o),d&&d._testMouseOver(a,b||k&&this,c||j&&this)}}},b._handleDoubleClick=function(a,b){var c=null,d=this._nextStage,e=this._getPointerData(-1);b||(c=this._getObjectsUnderPoint(e.x,e.y,null,!0),this._dispatchMouseEvent(c,"dblclick",!0,-1,e,a)),d&&d._handleDoubleClick(a,b||c&&this)},b._dispatchMouseEvent=function(a,b,c,d,e,f,g){if(a&&(c||a.hasEventListener(b))){var h=new createjs.MouseEvent(b,c,!1,e.x,e.y,f,d,d===this._primaryPointerID||-1===d,e.rawX,e.rawY,g);a.dispatchEvent(h)}},createjs.Stage=createjs.promote(a,"Container")}(),this.createjs=this.createjs||{},function(){function a(a){this.DisplayObject_constructor(),"string"==typeof a?(this.image=document.createElement("img"),this.image.src=a):this.image=a,this.sourceRect=null}var b=createjs.extend(a,createjs.DisplayObject);b.initialize=a,b.isVisible=function(){var a=this.image,b=this.cacheCanvas||a&&(a.naturalWidth||a.getContext||a.readyState>=2);return!!(this.visible&&this.alpha>0&&0!=this.scaleX&&0!=this.scaleY&&b)},b.draw=function(a,b){if(this.DisplayObject_draw(a,b)||!this.image)return!0;var c=this.image,d=this.sourceRect;if(d){var e=d.x,f=d.y,g=e+d.width,h=f+d.height,i=0,j=0,k=c.width,l=c.height;0>e&&(i-=e,e=0),g>k&&(g=k),0>f&&(j-=f,f=0),h>l&&(h=l),a.drawImage(c,e,f,g-e,h-f,i,j,g-e,h-f)}else a.drawImage(c,0,0);return!0},b.getBounds=function(){var a=this.DisplayObject_getBounds();if(a)return a;var b=this.image,c=this.sourceRect||b,d=b&&(b.naturalWidth||b.getContext||b.readyState>=2);return d?this._rectangle.setValues(0,0,c.width,c.height):null},b.clone=function(){var b=new a(this.image);return this.sourceRect&&(b.sourceRect=this.sourceRect.clone()),this._cloneProps(b),b},b.toString=function(){return"[Bitmap (name="+this.name+")]"},createjs.Bitmap=createjs.promote(a,"DisplayObject")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b){this.DisplayObject_constructor(),this.currentFrame=0,this.currentAnimation=null,this.paused=!0,this.spriteSheet=a,this.currentAnimationFrame=0,this.framerate=0,this._animation=null,this._currentFrame=null,this._skipAdvance=!1,null!=b&&this.gotoAndPlay(b)}var b=createjs.extend(a,createjs.DisplayObject);b.initialize=a,b.isVisible=function(){var a=this.cacheCanvas||this.spriteSheet.complete;return!!(this.visible&&this.alpha>0&&0!=this.scaleX&&0!=this.scaleY&&a)},b.draw=function(a,b){if(this.DisplayObject_draw(a,b))return!0;this._normalizeFrame();var c=this.spriteSheet.getFrame(0|this._currentFrame);if(!c)return!1;var d=c.rect;return d.width&&d.height&&a.drawImage(c.image,d.x,d.y,d.width,d.height,-c.regX,-c.regY,d.width,d.height),!0},b.play=function(){this.paused=!1},b.stop=function(){this.paused=!0},b.gotoAndPlay=function(a){this.paused=!1,this._skipAdvance=!0,this._goto(a)},b.gotoAndStop=function(a){this.paused=!0,this._goto(a)},b.advance=function(a){var b=this.framerate||this.spriteSheet.framerate,c=b&&null!=a?a/(1e3/b):1;this._normalizeFrame(c)},b.getBounds=function(){return this.DisplayObject_getBounds()||this.spriteSheet.getFrameBounds(this.currentFrame,this._rectangle)},b.clone=function(){return this._cloneProps(new a(this.spriteSheet))},b.toString=function(){return"[Sprite (name="+this.name+")]"},b._cloneProps=function(a){return this.DisplayObject__cloneProps(a),a.currentFrame=this.currentFrame,a.currentAnimation=this.currentAnimation,a.paused=this.paused,a.currentAnimationFrame=this.currentAnimationFrame,a.framerate=this.framerate,a._animation=this._animation,a._currentFrame=this._currentFrame,a._skipAdvance=this._skipAdvance,a},b._tick=function(a){this.paused||(this._skipAdvance||this.advance(a&&a.delta),this._skipAdvance=!1),this.DisplayObject__tick(a)},b._normalizeFrame=function(a){a=a||0;var b,c=this._animation,d=this.paused,e=this._currentFrame;if(c){var f=c.speed||1,g=this.currentAnimationFrame;if(b=c.frames.length,g+a*f>=b){var h=c.next;if(this._dispatchAnimationEnd(c,e,d,h,b-1))return;if(h)return this._goto(h,a-(b-g)/f);this.paused=!0,g=c.frames.length-1}else g+=a*f;this.currentAnimationFrame=g,this._currentFrame=c.frames[0|g]}else if(e=this._currentFrame+=a,b=this.spriteSheet.getNumFrames(),e>=b&&b>0&&!this._dispatchAnimationEnd(c,e,d,b-1)&&(this._currentFrame-=b)>=b)return this._normalizeFrame();e=0|this._currentFrame,this.currentFrame!=e&&(this.currentFrame=e,this.dispatchEvent("change"))},b._dispatchAnimationEnd=function(a,b,c,d,e){var f=a?a.name:null;if(this.hasEventListener("animationend")){var g=new createjs.Event("animationend");g.name=f,g.next=d,this.dispatchEvent(g)}var h=this._animation!=a||this._currentFrame!=b;return h||c||!this.paused||(this.currentAnimationFrame=e,h=!0),h},b._goto=function(a,b){if(this.currentAnimationFrame=0,isNaN(a)){var c=this.spriteSheet.getAnimation(a);c&&(this._animation=c,this.currentAnimation=a,this._normalizeFrame(b))}else this.currentAnimation=this._animation=null,this._currentFrame=a,this._normalizeFrame()},createjs.Sprite=createjs.promote(a,"DisplayObject")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.DisplayObject_constructor(),this.graphics=a?a:new createjs.Graphics}var b=createjs.extend(a,createjs.DisplayObject);b.isVisible=function(){var a=this.cacheCanvas||this.graphics&&!this.graphics.isEmpty();return!!(this.visible&&this.alpha>0&&0!=this.scaleX&&0!=this.scaleY&&a)},b.draw=function(a,b){return this.DisplayObject_draw(a,b)?!0:(this.graphics.draw(a,this),!0)},b.clone=function(b){var c=b&&this.graphics?this.graphics.clone():this.graphics;return this._cloneProps(new a(c))},b.toString=function(){return"[Shape (name="+this.name+")]"},createjs.Shape=createjs.promote(a,"DisplayObject")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.DisplayObject_constructor(),this.text=a,this.font=b,this.color=c,this.textAlign="left",this.textBaseline="top",this.maxWidth=null,this.outline=0,this.lineHeight=0,this.lineWidth=null}var b=createjs.extend(a,createjs.DisplayObject),c=createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");c.getContext&&(a._workingContext=c.getContext("2d"),c.width=c.height=1),a.H_OFFSETS={start:0,left:0,center:-.5,end:-1,right:-1},a.V_OFFSETS={top:0,hanging:-.01,middle:-.4,alphabetic:-.8,ideographic:-.85,bottom:-1},b.isVisible=function(){var a=this.cacheCanvas||null!=this.text&&""!==this.text;return!!(this.visible&&this.alpha>0&&0!=this.scaleX&&0!=this.scaleY&&a)},b.draw=function(a,b){if(this.DisplayObject_draw(a,b))return!0;var c=this.color||"#000";return this.outline?(a.strokeStyle=c,a.lineWidth=1*this.outline):a.fillStyle=c,this._drawText(this._prepContext(a)),!0},b.getMeasuredWidth=function(){return this._getMeasuredWidth(this.text)},b.getMeasuredLineHeight=function(){return 1.2*this._getMeasuredWidth("M")},b.getMeasuredHeight=function(){return this._drawText(null,{}).height},b.getBounds=function(){var b=this.DisplayObject_getBounds();if(b)return b;if(null==this.text||""===this.text)return null;var c=this._drawText(null,{}),d=this.maxWidth&&this.maxWidthj;j++){var l=i[j],m=null;if(null!=this.lineWidth&&(m=b.measureText(l).width)>this.lineWidth){var n=l.split(/(\s)/);l=n[0],m=b.measureText(l).width;for(var o=1,p=n.length;p>o;o+=2){var q=b.measureText(n[o]+n[o+1]).width;m+q>this.lineWidth?(e&&this._drawTextLine(b,l,h*f),d&&d.push(l),m>g&&(g=m),l=n[o+1],m=b.measureText(l).width,h++):(l+=n[o]+n[o+1],m+=q)}}e&&this._drawTextLine(b,l,h*f),d&&d.push(l),c&&null==m&&(m=b.measureText(l).width),m>g&&(g=m),h++}return c&&(c.width=g,c.height=h*f),e||b.restore(),c},b._drawTextLine=function(a,b,c){this.outline?a.strokeText(b,0,c,this.maxWidth||65535):a.fillText(b,0,c,this.maxWidth||65535)},b._getMeasuredWidth=function(b){var c=a._workingContext;c.save();var d=this._prepContext(c).measureText(b).width;return c.restore(),d},createjs.Text=createjs.promote(a,"DisplayObject")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b){this.Container_constructor(),this.text=a||"",this.spriteSheet=b,this.lineHeight=0,this.letterSpacing=0,this.spaceWidth=0,this._oldProps={text:0,spriteSheet:0,lineHeight:0,letterSpacing:0,spaceWidth:0}}var b=createjs.extend(a,createjs.Container);a.maxPoolSize=100,a._spritePool=[],b.draw=function(a,b){this.DisplayObject_draw(a,b)||(this._updateText(),this.Container_draw(a,b))},b.getBounds=function(){return this._updateText(),this.Container_getBounds()},b.isVisible=function(){var a=this.cacheCanvas||this.spriteSheet&&this.spriteSheet.complete&&this.text;return!!(this.visible&&this.alpha>0&&0!==this.scaleX&&0!==this.scaleY&&a)},b.clone=function(){return this._cloneProps(new a(this.text,this.spriteSheet))},b.addChild=b.addChildAt=b.removeChild=b.removeChildAt=b.removeAllChildren=function(){},b._cloneProps=function(a){return this.Container__cloneProps(a),a.lineHeight=this.lineHeight,a.letterSpacing=this.letterSpacing,a.spaceWidth=this.spaceWidth,a},b._getFrameIndex=function(a,b){var c,d=b.getAnimation(a);return d||(a!=(c=a.toUpperCase())||a!=(c=a.toLowerCase())||(c=null),c&&(d=b.getAnimation(c))),d&&d.frames[0]},b._getFrame=function(a,b){var c=this._getFrameIndex(a,b);return null==c?c:b.getFrame(c)},b._getLineHeight=function(a){var b=this._getFrame("1",a)||this._getFrame("T",a)||this._getFrame("L",a)||a.getFrame(0);return b?b.rect.height:1},b._getSpaceWidth=function(a){var b=this._getFrame("1",a)||this._getFrame("l",a)||this._getFrame("e",a)||this._getFrame("a",a)||a.getFrame(0);return b?b.rect.width:1},b._updateText=function(){var b,c=0,d=0,e=this._oldProps,f=!1,g=this.spaceWidth,h=this.lineHeight,i=this.spriteSheet,j=a._spritePool,k=this.children,l=0,m=k.length;for(var n in e)e[n]!=this[n]&&(e[n]=this[n],f=!0);if(f){var o=!!this._getFrame(" ",i);o||g||(g=this._getSpaceWidth(i)),h||(h=this._getLineHeight(i));for(var p=0,q=this.text.length;q>p;p++){var r=this.text.charAt(p);if(" "!=r||o)if("\n"!=r&&"\r"!=r){var s=this._getFrameIndex(r,i);null!=s&&(m>l?b=k[l]:(k.push(b=j.length?j.pop():new createjs.Sprite),b.parent=this,m++),b.spriteSheet=i,b.gotoAndStop(s),b.x=c,b.y=d,l++,c+=b.getBounds().width+this.letterSpacing)}else"\r"==r&&"\n"==this.text.charAt(p+1)&&p++,c=0,d+=h;else c+=g}for(;m>l;)j.push(b=k.pop()),b.parent=null,m--;j.length>a.maxPoolSize&&(j.length=a.maxPoolSize)}},createjs.BitmapText=createjs.promote(a,"Container")}(),this.createjs=this.createjs||{},function(){"use strict";function a(b,c,d,e){this.Container_constructor(),!a.inited&&a.init(),this.mode=b||a.INDEPENDENT,this.startPosition=c||0,this.loop=d,this.currentFrame=0,this.timeline=new createjs.Timeline(null,e,{paused:!0,position:c,useTicks:!0}),this.paused=!1,this.actionsEnabled=!0,this.autoReset=!0,this.frameBounds=this.frameBounds||null,this.framerate=null,this._synchOffset=0,this._prevPos=-1,this._prevPosition=0,this._t=0,this._managed={}}function b(){throw"MovieClipPlugin cannot be instantiated."}var c=createjs.extend(a,createjs.Container);a.INDEPENDENT="independent",a.SINGLE_FRAME="single",a.SYNCHED="synched",a.inited=!1,a.init=function(){a.inited||(b.install(),a.inited=!0)},c.getLabels=function(){return this.timeline.getLabels()},c.getCurrentLabel=function(){return this._updateTimeline(),this.timeline.getCurrentLabel()},c.getDuration=function(){return this.timeline.duration; +};try{Object.defineProperties(c,{labels:{get:c.getLabels},currentLabel:{get:c.getCurrentLabel},totalFrames:{get:c.getDuration},duration:{get:c.getDuration}})}catch(d){}c.initialize=a,c.isVisible=function(){return!!(this.visible&&this.alpha>0&&0!=this.scaleX&&0!=this.scaleY)},c.draw=function(a,b){return this.DisplayObject_draw(a,b)?!0:(this._updateTimeline(),this.Container_draw(a,b),!0)},c.play=function(){this.paused=!1},c.stop=function(){this.paused=!0},c.gotoAndPlay=function(a){this.paused=!1,this._goto(a)},c.gotoAndStop=function(a){this.paused=!0,this._goto(a)},c.advance=function(b){var c=a.INDEPENDENT;if(this.mode==c){for(var d=this,e=d.framerate;(d=d.parent)&&null==e;)d.mode==c&&(e=d._framerate);this._framerate=e;var f=null!=e&&-1!=e&&null!=b?b/(1e3/e)+this._t:1,g=0|f;for(this._t=f-g;!this.paused&&g--;)this._prevPosition=this._prevPos<0?0:this._prevPosition+1,this._updateTimeline()}},c.clone=function(){throw"MovieClip cannot be cloned."},c.toString=function(){return"[MovieClip (name="+this.name+")]"},c._tick=function(a){this.advance(a&&a.delta),this.Container__tick(a)},c._goto=function(a){var b=this.timeline.resolve(a);null!=b&&(-1==this._prevPos&&(this._prevPos=NaN),this._prevPosition=b,this._t=0,this._updateTimeline())},c._reset=function(){this._prevPos=-1,this._t=this.currentFrame=0,this.paused=!1},c._updateTimeline=function(){var b=this.timeline,c=this.mode!=a.INDEPENDENT;b.loop=null==this.loop?!0:this.loop;var d=c?this.startPosition+(this.mode==a.SINGLE_FRAME?0:this._synchOffset):this._prevPos<0?0:this._prevPosition,e=c||!this.actionsEnabled?createjs.Tween.NONE:null;if(this.currentFrame=b._calcPosition(d),b.setPosition(d,e),this._prevPosition=b._prevPosition,this._prevPos!=b._prevPos){this.currentFrame=this._prevPos=b._prevPos;for(var f in this._managed)this._managed[f]=1;for(var g=b._tweens,h=0,i=g.length;i>h;h++){var j=g[h],k=j._target;if(k!=this&&!j.passive){var l=j._stepPosition;k instanceof createjs.DisplayObject?this._addManagedChild(k,l):this._setState(k.state,l)}}var m=this.children;for(h=m.length-1;h>=0;h--){var n=m[h].id;1==this._managed[n]&&(this.removeChildAt(h),delete this._managed[n])}}},c._setState=function(a,b){if(a)for(var c=a.length-1;c>=0;c--){var d=a[c],e=d.t,f=d.p;for(var g in f)e[g]=f[g];this._addManagedChild(e,b)}},c._addManagedChild=function(b,c){b._off||(this.addChildAt(b,0),b instanceof a&&(b._synchOffset=c,b.mode==a.INDEPENDENT&&b.autoReset&&!this._managed[b.id]&&b._reset()),this._managed[b.id]=2)},c._getBounds=function(a,b){var c=this.DisplayObject_getBounds();return c||(this._updateTimeline(),this.frameBounds&&(c=this._rectangle.copy(this.frameBounds[this.currentFrame]))),c?this._transformBounds(c,a,b):this.Container__getBounds(a,b)},createjs.MovieClip=createjs.promote(a,"Container"),b.priority=100,b.install=function(){createjs.Tween.installPlugin(b,["startPosition"])},b.init=function(a,b,c){return c},b.step=function(){},b.tween=function(b,c,d,e,f,g,h,i){return b.target instanceof a?1==g?f[c]:e[c]:d}}(),this.createjs=this.createjs||{},function(){"use strict";function a(){throw"SpriteSheetUtils cannot be instantiated"}var b=createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");b.getContext&&(a._workingCanvas=b,a._workingContext=b.getContext("2d"),b.width=b.height=1),a.addFlippedFrames=function(b,c,d,e){if(c||d||e){var f=0;c&&a._flip(b,++f,!0,!1),d&&a._flip(b,++f,!1,!0),e&&a._flip(b,++f,!0,!0)}},a.extractFrame=function(b,c){isNaN(c)&&(c=b.getAnimation(c).frames[0]);var d=b.getFrame(c);if(!d)return null;var e=d.rect,f=a._workingCanvas;f.width=e.width,f.height=e.height,a._workingContext.drawImage(d.image,e.x,e.y,e.width,e.height,0,0,e.width,e.height);var g=document.createElement("img");return g.src=f.toDataURL("image/png"),g},a.mergeAlpha=function(a,b,c){c||(c=createjs.createCanvas?createjs.createCanvas():document.createElement("canvas")),c.width=Math.max(b.width,a.width),c.height=Math.max(b.height,a.height);var d=c.getContext("2d");return d.save(),d.drawImage(a,0,0),d.globalCompositeOperation="destination-in",d.drawImage(b,0,0),d.restore(),c},a._flip=function(b,c,d,e){for(var f=b._images,g=a._workingCanvas,h=a._workingContext,i=f.length/c,j=0;i>j;j++){var k=f[j];k.__tmp=j,h.setTransform(1,0,0,1,0,0),h.clearRect(0,0,g.width+1,g.height+1),g.width=k.width,g.height=k.height,h.setTransform(d?-1:1,0,0,e?-1:1,d?k.width:0,e?k.height:0),h.drawImage(k,0,0);var l=document.createElement("img");l.src=g.toDataURL("image/png"),l.width=k.width,l.height=k.height,f.push(l)}var m=b._frames,n=m.length/c;for(j=0;n>j;j++){k=m[j];var o=k.rect.clone();l=f[k.image.__tmp+i*c];var p={image:l,rect:o,regX:k.regX,regY:k.regY};d&&(o.x=l.width-o.x-o.width,p.regX=o.width-k.regX),e&&(o.y=l.height-o.y-o.height,p.regY=o.height-k.regY),m.push(p)}var q="_"+(d?"h":"")+(e?"v":""),r=b._animations,s=b._data,t=r.length/c;for(j=0;t>j;j++){var u=r[j];k=s[u];var v={name:u+q,speed:k.speed,next:k.next,frames:[]};k.next&&(v.next+=q),m=k.frames;for(var w=0,x=m.length;x>w;w++)v.frames.push(m[w]+n*c);s[v.name]=v,r.push(v.name)}},createjs.SpriteSheetUtils=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.EventDispatcher_constructor(),this.maxWidth=2048,this.maxHeight=2048,this.spriteSheet=null,this.scale=1,this.padding=1,this.timeSlice=.3,this.progress=-1,this.framerate=a||0,this._frames=[],this._animations={},this._data=null,this._nextFrameIndex=0,this._index=0,this._timerID=null,this._scale=1}var b=createjs.extend(a,createjs.EventDispatcher);a.ERR_DIMENSIONS="frame dimensions exceed max spritesheet dimensions",a.ERR_RUNNING="a build is already running",b.addFrame=function(b,c,d,e,f){if(this._data)throw a.ERR_RUNNING;var g=c||b.bounds||b.nominalBounds;return!g&&b.getBounds&&(g=b.getBounds()),g?(d=d||1,this._frames.push({source:b,sourceRect:g,scale:d,funct:e,data:f,index:this._frames.length,height:g.height*d})-1):null},b.addAnimation=function(b,c,d,e){if(this._data)throw a.ERR_RUNNING;this._animations[b]={frames:c,next:d,speed:e}},b.addMovieClip=function(b,c,d,e,f,g){if(this._data)throw a.ERR_RUNNING;var h=b.frameBounds,i=c||b.bounds||b.nominalBounds;if(!i&&b.getBounds&&(i=b.getBounds()),i||h){var j,k,l=this._frames.length,m=b.timeline.duration;for(j=0;m>j;j++){var n=h&&h[j]?h[j]:i;this.addFrame(b,n,d,this._setupMovieClipFrame,{i:j,f:e,d:f})}var o=b.timeline._labels,p=[];for(var q in o)p.push({index:o[q],label:q});if(p.length)for(p.sort(function(a,b){return a.index-b.index}),j=0,k=p.length;k>j;j++){for(var r=p[j].label,s=l+p[j].index,t=l+(j==k-1?m:p[j+1].index),u=[],v=s;t>v;v++)u.push(v);(!g||(r=g(r,b,s,t)))&&this.addAnimation(r,u,!0)}}},b.build=function(){if(this._data)throw a.ERR_RUNNING;for(this._startBuild();this._drawNext(););return this._endBuild(),this.spriteSheet},b.buildAsync=function(b){if(this._data)throw a.ERR_RUNNING;this.timeSlice=b,this._startBuild();var c=this;this._timerID=setTimeout(function(){c._run()},50-50*Math.max(.01,Math.min(.99,this.timeSlice||.3)))},b.stopAsync=function(){clearTimeout(this._timerID),this._data=null},b.clone=function(){throw"SpriteSheetBuilder cannot be cloned."},b.toString=function(){return"[SpriteSheetBuilder]"},b._startBuild=function(){var b=this.padding||0;this.progress=0,this.spriteSheet=null,this._index=0,this._scale=this.scale;var c=[];this._data={images:[],frames:c,framerate:this.framerate,animations:this._animations};var d=this._frames.slice();if(d.sort(function(a,b){return a.height<=b.height?-1:1}),d[d.length-1].height+2*b>this.maxHeight)throw a.ERR_DIMENSIONS;for(var e=0,f=0,g=0;d.length;){var h=this._fillRow(d,e,g,c,b);if(h.w>f&&(f=h.w),e+=h.h,!h.h||!d.length){var i=createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");i.width=this._getSize(f,this.maxWidth),i.height=this._getSize(e,this.maxHeight),this._data.images[g]=i,h.h||(f=e=0,g++)}}},b._setupMovieClipFrame=function(a,b){var c=a.actionsEnabled;a.actionsEnabled=!1,a.gotoAndStop(b.i),a.actionsEnabled=c,b.f&&b.f(a,b.d,b.i)},b._getSize=function(a,b){for(var c=4;Math.pow(2,++c)=0;l--){var m=b[l],n=this._scale*m.scale,o=m.sourceRect,p=m.source,q=Math.floor(n*o.x-f),r=Math.floor(n*o.y-f),s=Math.ceil(n*o.height+2*f),t=Math.ceil(n*o.width+2*f);if(t>g)throw a.ERR_DIMENSIONS;s>i||j+t>g||(m.img=d,m.rect=new createjs.Rectangle(j,c,t,s),k=k||s,b.splice(l,1),e[m.index]=[j,c,t,s,d,Math.round(-q+n*p.regX-f),Math.round(-r+n*p.regY-f)],j+=t)}return{w:j,h:k}},b._endBuild=function(){this.spriteSheet=new createjs.SpriteSheet(this._data),this._data=null,this.progress=1,this.dispatchEvent("complete")},b._run=function(){for(var a=50*Math.max(.01,Math.min(.99,this.timeSlice||.3)),b=(new Date).getTime()+a,c=!1;b>(new Date).getTime();)if(!this._drawNext()){c=!0;break}if(c)this._endBuild();else{var d=this;this._timerID=setTimeout(function(){d._run()},50-a)}var e=this.progress=this._index/this._frames.length;if(this.hasEventListener("progress")){var f=new createjs.Event("progress");f.progress=e,this.dispatchEvent(f)}},b._drawNext=function(){var a=this._frames[this._index],b=a.scale*this._scale,c=a.rect,d=a.sourceRect,e=this._data.images[a.img],f=e.getContext("2d");return a.funct&&a.funct(a.source,a.data),f.save(),f.beginPath(),f.rect(c.x,c.y,c.width,c.height),f.clip(),f.translate(Math.ceil(c.x-d.x*b),Math.ceil(c.y-d.y*b)),f.scale(b,b),a.source.draw(f),f.restore(),++this._indexa)&&(a=0),(isNaN(b)||0>b)&&(b=0),(isNaN(c)||1>c)&&(c=1),this.blurX=0|a,this.blurY=0|b,this.quality=0|c}var b=createjs.extend(a,createjs.Filter);a.MUL_TABLE=[1,171,205,293,57,373,79,137,241,27,391,357,41,19,283,265,497,469,443,421,25,191,365,349,335,161,155,149,9,278,269,261,505,245,475,231,449,437,213,415,405,395,193,377,369,361,353,345,169,331,325,319,313,307,301,37,145,285,281,69,271,267,263,259,509,501,493,243,479,118,465,459,113,446,55,435,429,423,209,413,51,403,199,393,97,3,379,375,371,367,363,359,355,351,347,43,85,337,333,165,327,323,5,317,157,311,77,305,303,75,297,294,73,289,287,71,141,279,277,275,68,135,67,133,33,262,260,129,511,507,503,499,495,491,61,121,481,477,237,235,467,232,115,457,227,451,7,445,221,439,218,433,215,427,425,211,419,417,207,411,409,203,202,401,399,396,197,49,389,387,385,383,95,189,47,187,93,185,23,183,91,181,45,179,89,177,11,175,87,173,345,343,341,339,337,21,167,83,331,329,327,163,81,323,321,319,159,79,315,313,39,155,309,307,153,305,303,151,75,299,149,37,295,147,73,291,145,289,287,143,285,71,141,281,35,279,139,69,275,137,273,17,271,135,269,267,133,265,33,263,131,261,130,259,129,257,1],a.SHG_TABLE=[0,9,10,11,9,12,10,11,12,9,13,13,10,9,13,13,14,14,14,14,10,13,14,14,14,13,13,13,9,14,14,14,15,14,15,14,15,15,14,15,15,15,14,15,15,15,15,15,14,15,15,15,15,15,15,12,14,15,15,13,15,15,15,15,16,16,16,15,16,14,16,16,14,16,13,16,16,16,15,16,13,16,15,16,14,9,16,16,16,16,16,16,16,16,16,13,14,16,16,15,16,16,10,16,15,16,14,16,16,14,16,16,14,16,16,14,15,16,16,16,14,15,14,15,13,16,16,15,17,17,17,17,17,17,14,15,17,17,16,16,17,16,15,17,16,17,11,17,16,17,16,17,16,17,17,16,17,17,16,17,17,16,16,17,17,17,16,14,17,17,17,17,15,16,14,16,15,16,13,16,15,16,14,16,15,16,12,16,15,16,17,17,17,17,17,13,16,15,17,17,17,16,15,17,17,17,16,15,17,17,14,16,17,17,16,17,17,16,15,17,16,14,17,16,15,17,16,17,17,16,17,15,16,17,14,17,16,15,17,16,17,13,17,16,17,17,16,17,14,17,16,17,16,17,16,17,9],b.getBounds=function(a){var b=0|this.blurX,c=0|this.blurY;if(0>=b&&0>=c)return a;var d=Math.pow(this.quality,.2);return(a||new createjs.Rectangle).pad(b*d+1,c*d+1,b*d+1,c*d+1)},b.clone=function(){return new a(this.blurX,this.blurY,this.quality)},b.toString=function(){return"[BlurFilter]"},b._applyFilter=function(b){var c=this.blurX>>1;if(isNaN(c)||0>c)return!1;var d=this.blurY>>1;if(isNaN(d)||0>d)return!1;if(0==c&&0==d)return!1;var e=this.quality;(isNaN(e)||1>e)&&(e=1),e|=0,e>3&&(e=3),1>e&&(e=1);var f=b.data,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=c+c+1|0,w=d+d+1|0,x=0|b.width,y=0|b.height,z=x-1|0,A=y-1|0,B=c+1|0,C=d+1|0,D={r:0,b:0,g:0,a:0},E=D;for(i=1;v>i;i++)E=E.n={r:0,b:0,g:0,a:0};E.n=D;var F={r:0,b:0,g:0,a:0},G=F;for(i=1;w>i;i++)G=G.n={r:0,b:0,g:0,a:0};G.n=F;for(var H=null,I=0|a.MUL_TABLE[c],J=0|a.SHG_TABLE[c],K=0|a.MUL_TABLE[d],L=0|a.SHG_TABLE[d];e-->0;){m=l=0;var M=I,N=J;for(h=y;--h>-1;){for(n=B*(r=f[0|l]),o=B*(s=f[l+1|0]),p=B*(t=f[l+2|0]),q=B*(u=f[l+3|0]),E=D,i=B;--i>-1;)E.r=r,E.g=s,E.b=t,E.a=u,E=E.n;for(i=1;B>i;i++)j=l+((i>z?z:i)<<2)|0,n+=E.r=f[j],o+=E.g=f[j+1],p+=E.b=f[j+2],q+=E.a=f[j+3],E=E.n;for(H=D,g=0;x>g;g++)f[l++]=n*M>>>N,f[l++]=o*M>>>N,f[l++]=p*M>>>N,f[l++]=q*M>>>N,j=m+((j=g+c+1)g;g++){for(l=g<<2|0,n=C*(r=f[l])|0,o=C*(s=f[l+1|0])|0,p=C*(t=f[l+2|0])|0,q=C*(u=f[l+3|0])|0,G=F,i=0;C>i;i++)G.r=r,G.g=s,G.b=t,G.a=u,G=G.n;for(k=x,i=1;d>=i;i++)l=k+g<<2,n+=G.r=f[l],o+=G.g=f[l+1],p+=G.b=f[l+2],q+=G.a=f[l+3],G=G.n,A>i&&(k+=x);if(l=g,H=F,e>0)for(h=0;y>h;h++)j=l<<2,f[j+3]=u=q*M>>>N,u>0?(f[j]=n*M>>>N,f[j+1]=o*M>>>N,f[j+2]=p*M>>>N):f[j]=f[j+1]=f[j+2]=0,j=g+((j=h+C)h;h++)j=l<<2,f[j+3]=u=q*M>>>N,u>0?(u=255/u,f[j]=(n*M>>>N)*u,f[j+1]=(o*M>>>N)*u,f[j+2]=(p*M>>>N)*u):f[j]=f[j+1]=f[j+2]=0,j=g+((j=h+C)d;d+=4)b[d+3]=c[d]||0;return!0},b._prepAlphaMap=function(){if(!this.alphaMap)return!1;if(this.alphaMap==this._alphaMap&&this._mapData)return!0;this._mapData=null;var a,b=this._alphaMap=this.alphaMap,c=b;b instanceof HTMLCanvasElement?a=c.getContext("2d"):(c=createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"),c.width=b.width,c.height=b.height,a=c.getContext("2d"),a.drawImage(b,0,0));try{var d=a.getImageData(0,0,b.width,b.height)}catch(e){return!1}return this._mapData=d.data,!0},createjs.AlphaMapFilter=createjs.promote(a,"Filter")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.mask=a}var b=createjs.extend(a,createjs.Filter);b.applyFilter=function(a,b,c,d,e,f,g,h){return this.mask?(f=f||a,null==g&&(g=b),null==h&&(h=c),f.save(),a!=f?!1:(f.globalCompositeOperation="destination-in",f.drawImage(this.mask,g,h),f.restore(),!0)):!0},b.clone=function(){return new a(this.mask)},b.toString=function(){return"[AlphaMaskFilter]"},createjs.AlphaMaskFilter=createjs.promote(a,"Filter")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c,d,e,f,g,h){this.redMultiplier=null!=a?a:1,this.greenMultiplier=null!=b?b:1,this.blueMultiplier=null!=c?c:1,this.alphaMultiplier=null!=d?d:1,this.redOffset=e||0,this.greenOffset=f||0,this.blueOffset=g||0,this.alphaOffset=h||0}var b=createjs.extend(a,createjs.Filter);b.toString=function(){return"[ColorFilter]"},b.clone=function(){return new a(this.redMultiplier,this.greenMultiplier,this.blueMultiplier,this.alphaMultiplier,this.redOffset,this.greenOffset,this.blueOffset,this.alphaOffset)},b._applyFilter=function(a){for(var b=a.data,c=b.length,d=0;c>d;d+=4)b[d]=b[d]*this.redMultiplier+this.redOffset,b[d+1]=b[d+1]*this.greenMultiplier+this.greenOffset,b[d+2]=b[d+2]*this.blueMultiplier+this.blueOffset,b[d+3]=b[d+3]*this.alphaMultiplier+this.alphaOffset;return!0},createjs.ColorFilter=createjs.promote(a,"Filter")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c,d){this.setColor(a,b,c,d)}var b=a.prototype;a.DELTA_INDEX=[0,.01,.02,.04,.05,.06,.07,.08,.1,.11,.12,.14,.15,.16,.17,.18,.2,.21,.22,.24,.25,.27,.28,.3,.32,.34,.36,.38,.4,.42,.44,.46,.48,.5,.53,.56,.59,.62,.65,.68,.71,.74,.77,.8,.83,.86,.89,.92,.95,.98,1,1.06,1.12,1.18,1.24,1.3,1.36,1.42,1.48,1.54,1.6,1.66,1.72,1.78,1.84,1.9,1.96,2,2.12,2.25,2.37,2.5,2.62,2.75,2.87,3,3.2,3.4,3.6,3.8,4,4.3,4.7,4.9,5,5.5,6,6.5,6.8,7,7.3,7.5,7.8,8,8.4,8.7,9,9.4,9.6,9.8,10],a.IDENTITY_MATRIX=[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1],a.LENGTH=a.IDENTITY_MATRIX.length,b.setColor=function(a,b,c,d){return this.reset().adjustColor(a,b,c,d)},b.reset=function(){return this.copy(a.IDENTITY_MATRIX)},b.adjustColor=function(a,b,c,d){return this.adjustHue(d),this.adjustContrast(b),this.adjustBrightness(a),this.adjustSaturation(c)},b.adjustBrightness=function(a){return 0==a||isNaN(a)?this:(a=this._cleanValue(a,255),this._multiplyMatrix([1,0,0,0,a,0,1,0,0,a,0,0,1,0,a,0,0,0,1,0,0,0,0,0,1]),this)},b.adjustContrast=function(b){if(0==b||isNaN(b))return this;b=this._cleanValue(b,100);var c;return 0>b?c=127+b/100*127:(c=b%1,c=0==c?a.DELTA_INDEX[b]:a.DELTA_INDEX[b<<0]*(1-c)+a.DELTA_INDEX[(b<<0)+1]*c,c=127*c+127),this._multiplyMatrix([c/127,0,0,0,.5*(127-c),0,c/127,0,0,.5*(127-c),0,0,c/127,0,.5*(127-c),0,0,0,1,0,0,0,0,0,1]),this},b.adjustSaturation=function(a){if(0==a||isNaN(a))return this;a=this._cleanValue(a,100);var b=1+(a>0?3*a/100:a/100),c=.3086,d=.6094,e=.082;return this._multiplyMatrix([c*(1-b)+b,d*(1-b),e*(1-b),0,0,c*(1-b),d*(1-b)+b,e*(1-b),0,0,c*(1-b),d*(1-b),e*(1-b)+b,0,0,0,0,0,1,0,0,0,0,0,1]),this},b.adjustHue=function(a){if(0==a||isNaN(a))return this;a=this._cleanValue(a,180)/180*Math.PI;var b=Math.cos(a),c=Math.sin(a),d=.213,e=.715,f=.072;return this._multiplyMatrix([d+b*(1-d)+c*-d,e+b*-e+c*-e,f+b*-f+c*(1-f),0,0,d+b*-d+.143*c,e+b*(1-e)+.14*c,f+b*-f+c*-.283,0,0,d+b*-d+c*-(1-d),e+b*-e+c*e,f+b*(1-f)+c*f,0,0,0,0,0,1,0,0,0,0,0,1]),this},b.concat=function(b){return b=this._fixMatrix(b),b.length!=a.LENGTH?this:(this._multiplyMatrix(b),this)},b.clone=function(){return(new a).copy(this)},b.toArray=function(){for(var b=[],c=0,d=a.LENGTH;d>c;c++)b[c]=this[c];return b},b.copy=function(b){for(var c=a.LENGTH,d=0;c>d;d++)this[d]=b[d];return this},b.toString=function(){return"[ColorMatrix]"},b._multiplyMatrix=function(a){var b,c,d,e=[];for(b=0;5>b;b++){for(c=0;5>c;c++)e[c]=this[c+5*b];for(c=0;5>c;c++){var f=0;for(d=0;5>d;d++)f+=a[c+5*d]*e[d];this[c+5*b]=f}}},b._cleanValue=function(a,b){return Math.min(b,Math.max(-b,a))},b._fixMatrix=function(b){return b instanceof a&&(b=b.toArray()),b.lengtha.LENGTH&&(b=b.slice(0,a.LENGTH)),b},createjs.ColorMatrix=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.matrix=a}var b=createjs.extend(a,createjs.Filter);b.toString=function(){return"[ColorMatrixFilter]"},b.clone=function(){return new a(this.matrix)},b._applyFilter=function(a){for(var b,c,d,e,f=a.data,g=f.length,h=this.matrix,i=h[0],j=h[1],k=h[2],l=h[3],m=h[4],n=h[5],o=h[6],p=h[7],q=h[8],r=h[9],s=h[10],t=h[11],u=h[12],v=h[13],w=h[14],x=h[15],y=h[16],z=h[17],A=h[18],B=h[19],C=0;g>C;C+=4)b=f[C],c=f[C+1],d=f[C+2],e=f[C+3],f[C]=b*i+c*j+d*k+e*l+m,f[C+1]=b*n+c*o+d*p+e*q+r,f[C+2]=b*s+c*t+d*u+e*v+w,f[C+3]=b*x+c*y+d*z+e*A+B;return!0},createjs.ColorMatrixFilter=createjs.promote(a,"Filter")}(),this.createjs=this.createjs||{},function(){"use strict";function a(){throw"Touch cannot be instantiated"}a.isSupported=function(){return!!("ontouchstart"in window||window.navigator.msPointerEnabled&&window.navigator.msMaxTouchPoints>0||window.navigator.pointerEnabled&&window.navigator.maxTouchPoints>0)},a.enable=function(b,c,d){return b&&b.canvas&&a.isSupported()?b.__touch?!0:(b.__touch={pointers:{},multitouch:!c,preventDefault:!d,count:0},"ontouchstart"in window?a._IOS_enable(b):(window.navigator.msPointerEnabled||window.navigator.pointerEnabled)&&a._IE_enable(b),!0):!1},a.disable=function(b){b&&("ontouchstart"in window?a._IOS_disable(b):(window.navigator.msPointerEnabled||window.navigator.pointerEnabled)&&a._IE_disable(b),delete b.__touch)},a._IOS_enable=function(b){var c=b.canvas,d=b.__touch.f=function(c){a._IOS_handleEvent(b,c)};c.addEventListener("touchstart",d,!1),c.addEventListener("touchmove",d,!1),c.addEventListener("touchend",d,!1),c.addEventListener("touchcancel",d,!1)},a._IOS_disable=function(a){var b=a.canvas;if(b){var c=a.__touch.f;b.removeEventListener("touchstart",c,!1),b.removeEventListener("touchmove",c,!1),b.removeEventListener("touchend",c,!1),b.removeEventListener("touchcancel",c,!1)}},a._IOS_handleEvent=function(a,b){if(a){a.__touch.preventDefault&&b.preventDefault&&b.preventDefault();for(var c=b.changedTouches,d=b.type,e=0,f=c.length;f>e;e++){var g=c[e],h=g.identifier;g.target==a.canvas&&("touchstart"==d?this._handleStart(a,h,b,g.pageX,g.pageY):"touchmove"==d?this._handleMove(a,h,b,g.pageX,g.pageY):("touchend"==d||"touchcancel"==d)&&this._handleEnd(a,h,b))}}},a._IE_enable=function(b){var c=b.canvas,d=b.__touch.f=function(c){a._IE_handleEvent(b,c)};void 0===window.navigator.pointerEnabled?(c.addEventListener("MSPointerDown",d,!1),window.addEventListener("MSPointerMove",d,!1),window.addEventListener("MSPointerUp",d,!1),window.addEventListener("MSPointerCancel",d,!1),b.__touch.preventDefault&&(c.style.msTouchAction="none")):(c.addEventListener("pointerdown",d,!1),window.addEventListener("pointermove",d,!1),window.addEventListener("pointerup",d,!1),window.addEventListener("pointercancel",d,!1),b.__touch.preventDefault&&(c.style.touchAction="none")),b.__touch.activeIDs={}},a._IE_disable=function(a){var b=a.__touch.f;void 0===window.navigator.pointerEnabled?(window.removeEventListener("MSPointerMove",b,!1),window.removeEventListener("MSPointerUp",b,!1),window.removeEventListener("MSPointerCancel",b,!1),a.canvas&&a.canvas.removeEventListener("MSPointerDown",b,!1)):(window.removeEventListener("pointermove",b,!1),window.removeEventListener("pointerup",b,!1),window.removeEventListener("pointercancel",b,!1),a.canvas&&a.canvas.removeEventListener("pointerdown",b,!1))},a._IE_handleEvent=function(a,b){if(a){a.__touch.preventDefault&&b.preventDefault&&b.preventDefault();var c=b.type,d=b.pointerId,e=a.__touch.activeIDs;if("MSPointerDown"==c||"pointerdown"==c){if(b.srcElement!=a.canvas)return;e[d]=!0,this._handleStart(a,d,b,b.pageX,b.pageY)}else e[d]&&("MSPointerMove"==c||"pointermove"==c?this._handleMove(a,d,b,b.pageX,b.pageY):("MSPointerUp"==c||"MSPointerCancel"==c||"pointerup"==c||"pointercancel"==c)&&(delete e[d],this._handleEnd(a,d,b)))}},a._handleStart=function(a,b,c,d,e){var f=a.__touch;if(f.multitouch||!f.count){var g=f.pointers;g[b]||(g[b]=!0,f.count++,a._handlePointerDown(b,c,d,e))}},a._handleMove=function(a,b,c,d,e){a.__touch.pointers[b]&&a._handlePointerMove(b,c,d,e)},a._handleEnd=function(a,b,c){var d=a.__touch,e=d.pointers;e[b]&&(d.count--,a._handlePointerUp(b,c,!0),delete e[b])},createjs.Touch=a}(),this.createjs=this.createjs||{},function(){"use strict";var a=createjs.EaselJS=createjs.EaselJS||{};a.version="0.8.2",a.buildDate="Thu, 26 Nov 2015 20:44:34 GMT"}(),this.createjs=this.createjs||{},function(){"use strict";var a=createjs.PreloadJS=createjs.PreloadJS||{};a.version="0.6.2",a.buildDate="Thu, 26 Nov 2015 20:44:31 GMT"}(),this.createjs=this.createjs||{},function(){"use strict";createjs.proxy=function(a,b){var c=Array.prototype.slice.call(arguments,2);return function(){return a.apply(b,Array.prototype.slice.call(arguments,0).concat(c))}}}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.Event_constructor("error"),this.title=a,this.message=b,this.data=c}var b=createjs.extend(a,createjs.Event);b.clone=function(){return new createjs.ErrorEvent(this.title,this.message,this.data)},createjs.ErrorEvent=createjs.promote(a,"Event")}(),this.createjs=this.createjs||{},function(a){"use strict";function b(a,b){this.Event_constructor("progress"),this.loaded=a,this.total=null==b?1:b,this.progress=0==b?0:this.loaded/this.total}var c=createjs.extend(b,createjs.Event);c.clone=function(){return new createjs.ProgressEvent(this.loaded,this.total)},createjs.ProgressEvent=createjs.promote(b,"Event")}(window),function(){function a(b,d){function f(a){if(f[a]!==q)return f[a];var b;if("bug-string-char-index"==a)b="a"!="a"[0];else if("json"==a)b=f("json-stringify")&&f("json-parse");else{var c,e='{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}';if("json-stringify"==a){var i=d.stringify,k="function"==typeof i&&t;if(k){(c=function(){return 1}).toJSON=c;try{k="0"===i(0)&&"0"===i(new g)&&'""'==i(new h)&&i(s)===q&&i(q)===q&&i()===q&&"1"===i(c)&&"[1]"==i([c])&&"[null]"==i([q])&&"null"==i(null)&&"[null,null,null]"==i([q,s,null])&&i({a:[c,!0,!1,null,"\x00\b\n\f\r "]})==e&&"1"===i(null,c)&&"[\n 1,\n 2\n]"==i([1,2],null,1)&&'"-271821-04-20T00:00:00.000Z"'==i(new j(-864e13))&&'"+275760-09-13T00:00:00.000Z"'==i(new j(864e13))&&'"-000001-01-01T00:00:00.000Z"'==i(new j(-621987552e5))&&'"1969-12-31T23:59:59.999Z"'==i(new j(-1))}catch(l){k=!1}}b=k}if("json-parse"==a){var m=d.parse;if("function"==typeof m)try{if(0===m("0")&&!m(!1)){c=m(e);var n=5==c.a.length&&1===c.a[0];if(n){try{n=!m('" "')}catch(l){}if(n)try{n=1!==m("01")}catch(l){}if(n)try{n=1!==m("1.")}catch(l){}}}}catch(l){n=!1}b=n}}return f[a]=!!b}b||(b=e.Object()),d||(d=e.Object());var g=b.Number||e.Number,h=b.String||e.String,i=b.Object||e.Object,j=b.Date||e.Date,k=b.SyntaxError||e.SyntaxError,l=b.TypeError||e.TypeError,m=b.Math||e.Math,n=b.JSON||e.JSON;"object"==typeof n&&n&&(d.stringify=n.stringify,d.parse=n.parse);var o,p,q,r=i.prototype,s=r.toString,t=new j(-0xc782b5b800cec);try{t=-109252==t.getUTCFullYear()&&0===t.getUTCMonth()&&1===t.getUTCDate()&&10==t.getUTCHours()&&37==t.getUTCMinutes()&&6==t.getUTCSeconds()&&708==t.getUTCMilliseconds()}catch(u){}if(!f("json")){var v="[object Function]",w="[object Date]",x="[object Number]",y="[object String]",z="[object Array]",A="[object Boolean]",B=f("bug-string-char-index");if(!t)var C=m.floor,D=[0,31,59,90,120,151,181,212,243,273,304,334],E=function(a,b){return D[b]+365*(a-1970)+C((a-1969+(b=+(b>1)))/4)-C((a-1901+b)/100)+C((a-1601+b)/400)};if((o=r.hasOwnProperty)||(o=function(a){var b,c={};return(c.__proto__=null,c.__proto__={toString:1},c).toString!=s?o=function(a){var b=this.__proto__,c=a in(this.__proto__=null,this);return this.__proto__=b,c}:(b=c.constructor,o=function(a){var c=(this.constructor||b).prototype;return a in this&&!(a in c&&this[a]===c[a])}),c=null,o.call(this,a)}),p=function(a,b){var d,e,f,g=0;(d=function(){this.valueOf=0}).prototype.valueOf=0,e=new d;for(f in e)o.call(e,f)&&g++;return d=e=null,g?p=2==g?function(a,b){var c,d={},e=s.call(a)==v;for(c in a)e&&"prototype"==c||o.call(d,c)||!(d[c]=1)||!o.call(a,c)||b(c)}:function(a,b){var c,d,e=s.call(a)==v;for(c in a)e&&"prototype"==c||!o.call(a,c)||(d="constructor"===c)||b(c);(d||o.call(a,c="constructor"))&&b(c)}:(e=["valueOf","toString","toLocaleString","propertyIsEnumerable","isPrototypeOf","hasOwnProperty","constructor"],p=function(a,b){var d,f,g=s.call(a)==v,h=!g&&"function"!=typeof a.constructor&&c[typeof a.hasOwnProperty]&&a.hasOwnProperty||o;for(d in a)g&&"prototype"==d||!h.call(a,d)||b(d);for(f=e.length;d=e[--f];h.call(a,d)&&b(d));}),p(a,b)},!f("json-stringify")){var F={92:"\\\\",34:'\\"',8:"\\b",12:"\\f",10:"\\n",13:"\\r",9:"\\t"},G="000000",H=function(a,b){return(G+(b||0)).slice(-a)},I="\\u00",J=function(a){for(var b='"',c=0,d=a.length,e=!B||d>10,f=e&&(B?a.split(""):a);d>c;c++){var g=a.charCodeAt(c);switch(g){case 8:case 9:case 10:case 12:case 13:case 34:case 92:b+=F[g];break;default:if(32>g){b+=I+H(2,g.toString(16));break}b+=e?f[c]:a.charAt(c)}}return b+'"'},K=function(a,b,c,d,e,f,g){var h,i,j,k,m,n,r,t,u,v,B,D,F,G,I,L;try{h=b[a]}catch(M){}if("object"==typeof h&&h)if(i=s.call(h),i!=w||o.call(h,"toJSON"))"function"==typeof h.toJSON&&(i!=x&&i!=y&&i!=z||o.call(h,"toJSON"))&&(h=h.toJSON(a));else if(h>-1/0&&1/0>h){if(E){for(m=C(h/864e5),j=C(m/365.2425)+1970-1;E(j+1,0)<=m;j++);for(k=C((m-E(j,0))/30.42);E(j,k+1)<=m;k++);m=1+m-E(j,k),n=(h%864e5+864e5)%864e5,r=C(n/36e5)%24,t=C(n/6e4)%60,u=C(n/1e3)%60,v=n%1e3}else j=h.getUTCFullYear(),k=h.getUTCMonth(),m=h.getUTCDate(),r=h.getUTCHours(),t=h.getUTCMinutes(),u=h.getUTCSeconds(),v=h.getUTCMilliseconds();h=(0>=j||j>=1e4?(0>j?"-":"+")+H(6,0>j?-j:j):H(4,j))+"-"+H(2,k+1)+"-"+H(2,m)+"T"+H(2,r)+":"+H(2,t)+":"+H(2,u)+"."+H(3,v)+"Z"}else h=null;if(c&&(h=c.call(b,a,h)),null===h)return"null";if(i=s.call(h),i==A)return""+h;if(i==x)return h>-1/0&&1/0>h?""+h:"null";if(i==y)return J(""+h);if("object"==typeof h){for(G=g.length;G--;)if(g[G]===h)throw l();if(g.push(h),B=[],I=f,f+=e,i==z){for(F=0,G=h.length;G>F;F++)D=K(F,h,c,d,e,f,g),B.push(D===q?"null":D);L=B.length?e?"[\n"+f+B.join(",\n"+f)+"\n"+I+"]":"["+B.join(",")+"]":"[]"}else p(d||h,function(a){var b=K(a,h,c,d,e,f,g);b!==q&&B.push(J(a)+":"+(e?" ":"")+b)}),L=B.length?e?"{\n"+f+B.join(",\n"+f)+"\n"+I+"}":"{"+B.join(",")+"}":"{}";return g.pop(),L}};d.stringify=function(a,b,d){var e,f,g,h;if(c[typeof b]&&b)if((h=s.call(b))==v)f=b;else if(h==z){g={};for(var i,j=0,k=b.length;k>j;i=b[j++],h=s.call(i),(h==y||h==x)&&(g[i]=1));}if(d)if((h=s.call(d))==x){if((d-=d%1)>0)for(e="",d>10&&(d=10);e.lengthL;)switch(e=f.charCodeAt(L)){case 9:case 10:case 13:case 32:L++;break;case 123:case 125:case 91:case 93:case 58:case 44:return a=B?f.charAt(L):f[L],L++,a;case 34:for(a="@",L++;g>L;)if(e=f.charCodeAt(L),32>e)P();else if(92==e)switch(e=f.charCodeAt(++L)){case 92:case 34: +case 47:case 98:case 116:case 110:case 102:case 114:a+=O[e],L++;break;case 117:for(b=++L,c=L+4;c>L;L++)e=f.charCodeAt(L),e>=48&&57>=e||e>=97&&102>=e||e>=65&&70>=e||P();a+=N("0x"+f.slice(b,L));break;default:P()}else{if(34==e)break;for(e=f.charCodeAt(L),b=L;e>=32&&92!=e&&34!=e;)e=f.charCodeAt(++L);a+=f.slice(b,L)}if(34==f.charCodeAt(L))return L++,a;P();default:if(b=L,45==e&&(d=!0,e=f.charCodeAt(++L)),e>=48&&57>=e){for(48==e&&(e=f.charCodeAt(L+1),e>=48&&57>=e)&&P(),d=!1;g>L&&(e=f.charCodeAt(L),e>=48&&57>=e);L++);if(46==f.charCodeAt(L)){for(c=++L;g>c&&(e=f.charCodeAt(c),e>=48&&57>=e);c++);c==L&&P(),L=c}if(e=f.charCodeAt(L),101==e||69==e){for(e=f.charCodeAt(++L),(43==e||45==e)&&L++,c=L;g>c&&(e=f.charCodeAt(c),e>=48&&57>=e);c++);c==L&&P(),L=c}return+f.slice(b,L)}if(d&&P(),"true"==f.slice(L,L+4))return L+=4,!0;if("false"==f.slice(L,L+5))return L+=5,!1;if("null"==f.slice(L,L+4))return L+=4,null;P()}return"$"},R=function(a){var b,c;if("$"==a&&P(),"string"==typeof a){if("@"==(B?a.charAt(0):a[0]))return a.slice(1);if("["==a){for(b=[];a=Q(),"]"!=a;c||(c=!0))c&&(","==a?(a=Q(),"]"==a&&P()):P()),","==a&&P(),b.push(R(a));return b}if("{"==a){for(b={};a=Q(),"}"!=a;c||(c=!0))c&&(","==a?(a=Q(),"}"==a&&P()):P()),(","==a||"string"!=typeof a||"@"!=(B?a.charAt(0):a[0])||":"!=Q())&&P(),b[a.slice(1)]=R(Q());return b}P()}return a},S=function(a,b,c){var d=T(a,b,c);d===q?delete a[b]:a[b]=d},T=function(a,b,c){var d,e=a[b];if("object"==typeof e&&e)if(s.call(e)==z)for(d=e.length;d--;)S(e,d,c);else p(e,function(a){S(e,a,c)});return c.call(a,b,e)};d.parse=function(a,b){var c,d;return L=0,M=""+a,c=R(Q()),"$"!=Q()&&P(),L=M=null,b&&s.call(b)==v?T((d={},d[""]=c,d),"",b):c}}}return d.runInContext=a,d}var b="function"==typeof define&&define.amd,c={"function":!0,object:!0},d=c[typeof exports]&&exports&&!exports.nodeType&&exports,e=c[typeof window]&&window||this,f=d&&c[typeof module]&&module&&!module.nodeType&&"object"==typeof global&&global;if(!f||f.global!==f&&f.window!==f&&f.self!==f||(e=f),d&&!b)a(e,d);else{var g=e.JSON,h=e.JSON3,i=!1,j=a(e,e.JSON3={noConflict:function(){return i||(i=!0,e.JSON=g,e.JSON3=h,g=h=null),j}});e.JSON={parse:j.parse,stringify:j.stringify}}b&&define(function(){return j})}.call(this),function(){var a={};a.appendToHead=function(b){a.getHead().appendChild(b)},a.getHead=function(){return document.head||document.getElementsByTagName("head")[0]},a.getBody=function(){return document.body||document.getElementsByTagName("body")[0]},createjs.DomUtils=a}(),function(){var a={};a.parseXML=function(a,b){var c=null;try{if(window.DOMParser){var d=new DOMParser;c=d.parseFromString(a,b)}}catch(e){}if(!c)try{c=new ActiveXObject("Microsoft.XMLDOM"),c.async=!1,c.loadXML(a)}catch(e){c=null}return c},a.parseJSON=function(a){if(null==a)return null;try{return JSON.parse(a)}catch(b){throw b}},createjs.DataUtils=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(){this.src=null,this.type=null,this.id=null,this.maintainOrder=!1,this.callback=null,this.data=null,this.method=createjs.LoadItem.GET,this.values=null,this.headers=null,this.withCredentials=!1,this.mimeType=null,this.crossOrigin=null,this.loadTimeout=c.LOAD_TIMEOUT_DEFAULT}var b=a.prototype={},c=a;c.LOAD_TIMEOUT_DEFAULT=8e3,c.create=function(b){if("string"==typeof b){var d=new a;return d.src=b,d}if(b instanceof c)return b;if(b instanceof Object&&b.src)return null==b.loadTimeout&&(b.loadTimeout=c.LOAD_TIMEOUT_DEFAULT),b;throw new Error("Type not recognized.")},b.set=function(a){for(var b in a)this[b]=a[b];return this},createjs.LoadItem=c}(),function(){var a={};a.ABSOLUTE_PATT=/^(?:\w+:)?\/{2}/i,a.RELATIVE_PATT=/^[.\/]*?\//i,a.EXTENSION_PATT=/\/?[^\/]+\.(\w{1,5})$/i,a.parseURI=function(b){var c={absolute:!1,relative:!1};if(null==b)return c;var d=b.indexOf("?");d>-1&&(b=b.substr(0,d));var e;return a.ABSOLUTE_PATT.test(b)?c.absolute=!0:a.RELATIVE_PATT.test(b)&&(c.relative=!0),(e=b.match(a.EXTENSION_PATT))&&(c.extension=e[1].toLowerCase()),c},a.formatQueryString=function(a,b){if(null==a)throw new Error("You must specify data.");var c=[];for(var d in a)c.push(d+"="+escape(a[d]));return b&&(c=c.concat(b)),c.join("&")},a.buildPath=function(a,b){if(null==b)return a;var c=[],d=a.indexOf("?");if(-1!=d){var e=a.slice(d+1);c=c.concat(e.split("&"))}return-1!=d?a.slice(0,d)+"?"+this.formatQueryString(b,c):a+"?"+this.formatQueryString(b,c)},a.isCrossDomain=function(a){var b=document.createElement("a");b.href=a.src;var c=document.createElement("a");c.href=location.href;var d=""!=b.hostname&&(b.port!=c.port||b.protocol!=c.protocol||b.hostname!=c.hostname);return d},a.isLocal=function(a){var b=document.createElement("a");return b.href=a.src,""==b.hostname&&"file:"==b.protocol},a.isBinary=function(a){switch(a){case createjs.AbstractLoader.IMAGE:case createjs.AbstractLoader.BINARY:return!0;default:return!1}},a.isImageTag=function(a){return a instanceof HTMLImageElement},a.isAudioTag=function(a){return window.HTMLAudioElement?a instanceof HTMLAudioElement:!1},a.isVideoTag=function(a){return window.HTMLVideoElement?a instanceof HTMLVideoElement:!1},a.isText=function(a){switch(a){case createjs.AbstractLoader.TEXT:case createjs.AbstractLoader.JSON:case createjs.AbstractLoader.MANIFEST:case createjs.AbstractLoader.XML:case createjs.AbstractLoader.CSS:case createjs.AbstractLoader.SVG:case createjs.AbstractLoader.JAVASCRIPT:case createjs.AbstractLoader.SPRITESHEET:return!0;default:return!1}},a.getTypeByExtension=function(a){if(null==a)return createjs.AbstractLoader.TEXT;switch(a.toLowerCase()){case"jpeg":case"jpg":case"gif":case"png":case"webp":case"bmp":return createjs.AbstractLoader.IMAGE;case"ogg":case"mp3":case"webm":return createjs.AbstractLoader.SOUND;case"mp4":case"webm":case"ts":return createjs.AbstractLoader.VIDEO;case"json":return createjs.AbstractLoader.JSON;case"xml":return createjs.AbstractLoader.XML;case"css":return createjs.AbstractLoader.CSS;case"js":return createjs.AbstractLoader.JAVASCRIPT;case"svg":return createjs.AbstractLoader.SVG;default:return createjs.AbstractLoader.TEXT}},createjs.RequestUtils=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.EventDispatcher_constructor(),this.loaded=!1,this.canceled=!1,this.progress=0,this.type=c,this.resultFormatter=null,a?this._item=createjs.LoadItem.create(a):this._item=null,this._preferXHR=b,this._result=null,this._rawResult=null,this._loadedItems=null,this._tagSrcAttribute=null,this._tag=null}var b=createjs.extend(a,createjs.EventDispatcher),c=a;c.POST="POST",c.GET="GET",c.BINARY="binary",c.CSS="css",c.IMAGE="image",c.JAVASCRIPT="javascript",c.JSON="json",c.JSONP="jsonp",c.MANIFEST="manifest",c.SOUND="sound",c.VIDEO="video",c.SPRITESHEET="spritesheet",c.SVG="svg",c.TEXT="text",c.XML="xml",b.getItem=function(){return this._item},b.getResult=function(a){return a?this._rawResult:this._result},b.getTag=function(){return this._tag},b.setTag=function(a){this._tag=a},b.load=function(){this._createRequest(),this._request.on("complete",this,this),this._request.on("progress",this,this),this._request.on("loadStart",this,this),this._request.on("abort",this,this),this._request.on("timeout",this,this),this._request.on("error",this,this);var a=new createjs.Event("initialize");a.loader=this._request,this.dispatchEvent(a),this._request.load()},b.cancel=function(){this.canceled=!0,this.destroy()},b.destroy=function(){this._request&&(this._request.removeAllEventListeners(),this._request.destroy()),this._request=null,this._item=null,this._rawResult=null,this._result=null,this._loadItems=null,this.removeAllEventListeners()},b.getLoadedItems=function(){return this._loadedItems},b._createRequest=function(){this._preferXHR?this._request=new createjs.XHRRequest(this._item):this._request=new createjs.TagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},b._createTag=function(a){return null},b._sendLoadStart=function(){this._isCanceled()||this.dispatchEvent("loadstart")},b._sendProgress=function(a){if(!this._isCanceled()){var b=null;"number"==typeof a?(this.progress=a,b=new createjs.ProgressEvent(this.progress)):(b=a,this.progress=a.loaded/a.total,b.progress=this.progress,(isNaN(this.progress)||this.progress==1/0)&&(this.progress=0)),this.hasEventListener("progress")&&this.dispatchEvent(b)}},b._sendComplete=function(){if(!this._isCanceled()){this.loaded=!0;var a=new createjs.Event("complete");a.rawResult=this._rawResult,null!=this._result&&(a.result=this._result),this.dispatchEvent(a)}},b._sendError=function(a){!this._isCanceled()&&this.hasEventListener("error")&&(null==a&&(a=new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY")),this.dispatchEvent(a))},b._isCanceled=function(){return null==window.createjs||this.canceled?!0:!1},b.resultFormatter=null,b.handleEvent=function(a){switch(a.type){case"complete":this._rawResult=a.target._response;var b=this.resultFormatter&&this.resultFormatter(this);b instanceof Function?b.call(this,createjs.proxy(this._resultFormatSuccess,this),createjs.proxy(this._resultFormatFailed,this)):(this._result=b||this._rawResult,this._sendComplete());break;case"progress":this._sendProgress(a);break;case"error":this._sendError(a);break;case"loadstart":this._sendLoadStart();break;case"abort":case"timeout":this._isCanceled()||this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_"+a.type.toUpperCase()+"_ERROR"))}},b._resultFormatSuccess=function(a){this._result=a,this._sendComplete()},b._resultFormatFailed=function(a){this._sendError(a)},b.buildPath=function(a,b){return createjs.RequestUtils.buildPath(a,b)},b.toString=function(){return"[PreloadJS AbstractLoader]"},createjs.AbstractLoader=createjs.promote(a,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.AbstractLoader_constructor(a,b,c),this.resultFormatter=this._formatResult,this._tagSrcAttribute="src",this.on("initialize",this._updateXHR,this)}var b=createjs.extend(a,createjs.AbstractLoader);b.load=function(){this._tag||(this._tag=this._createTag(this._item.src)),this._tag.preload="auto",this._tag.load(),this.AbstractLoader_load()},b._createTag=function(){},b._createRequest=function(){this._preferXHR?this._request=new createjs.XHRRequest(this._item):this._request=new createjs.MediaTagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},b._updateXHR=function(a){a.loader.setResponseType&&a.loader.setResponseType("blob")},b._formatResult=function(a){if(this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.onstalled=null,this._preferXHR){var b=window.URL||window.webkitURL,c=a.getResult(!0);a.getTag().src=b.createObjectURL(c)}return a.getTag()},createjs.AbstractMediaLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";var a=function(a){this._item=a},b=createjs.extend(a,createjs.EventDispatcher);b.load=function(){},b.destroy=function(){},b.cancel=function(){},createjs.AbstractRequest=createjs.promote(a,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this),this._addedToDOM=!1,this._startTagVisibility=null}var b=createjs.extend(a,createjs.AbstractRequest);b.load=function(){this._tag.onload=createjs.proxy(this._handleTagComplete,this),this._tag.onreadystatechange=createjs.proxy(this._handleReadyStateChange,this),this._tag.onerror=createjs.proxy(this._handleError,this);var a=new createjs.Event("initialize");a.loader=this._tag,this.dispatchEvent(a),this._hideTag(),this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout),this._tag[this._tagSrcAttribute]=this._item.src,null==this._tag.parentNode&&(window.document.body.appendChild(this._tag),this._addedToDOM=!0)},b.destroy=function(){this._clean(),this._tag=null,this.AbstractRequest_destroy()},b._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},b._handleError=function(){this._clean(),this.dispatchEvent("error")},b._handleTagComplete=function(){this._rawResult=this._tag,this._result=this.resultFormatter&&this.resultFormatter(this)||this._rawResult,this._clean(),this._showTag(),this.dispatchEvent("complete")},b._handleTimeout=function(){this._clean(),this.dispatchEvent(new createjs.Event("timeout"))},b._clean=function(){this._tag.onload=null,this._tag.onreadystatechange=null,this._tag.onerror=null,this._addedToDOM&&null!=this._tag.parentNode&&this._tag.parentNode.removeChild(this._tag),clearTimeout(this._loadTimeout)},b._hideTag=function(){this._startTagVisibility=this._tag.style.visibility,this._tag.style.visibility="hidden"},b._showTag=function(){this._tag.style.visibility=this._startTagVisibility},b._handleStalled=function(){},createjs.TagRequest=createjs.promote(a,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this)}var b=createjs.extend(a,createjs.TagRequest);b.load=function(){var a=createjs.proxy(this._handleStalled,this);this._stalledCallback=a;var b=createjs.proxy(this._handleProgress,this);this._handleProgress=b,this._tag.addEventListener("stalled",a),this._tag.addEventListener("progress",b),this._tag.addEventListener&&this._tag.addEventListener("canplaythrough",this._loadedHandler,!1),this.TagRequest_load()},b._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},b._handleStalled=function(){},b._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},b._clean=function(){this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.removeEventListener("stalled",this._stalledCallback),this._tag.removeEventListener("progress",this._progressCallback),this.TagRequest__clean()},createjs.MediaTagRequest=createjs.promote(a,"TagRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractRequest_constructor(a),this._request=null,this._loadTimeout=null,this._xhrLevel=1,this._response=null,this._rawResponse=null,this._canceled=!1,this._handleLoadStartProxy=createjs.proxy(this._handleLoadStart,this),this._handleProgressProxy=createjs.proxy(this._handleProgress,this),this._handleAbortProxy=createjs.proxy(this._handleAbort,this),this._handleErrorProxy=createjs.proxy(this._handleError,this),this._handleTimeoutProxy=createjs.proxy(this._handleTimeout,this),this._handleLoadProxy=createjs.proxy(this._handleLoad,this),this._handleReadyStateChangeProxy=createjs.proxy(this._handleReadyStateChange,this),!this._createXHR(a)}var b=createjs.extend(a,createjs.AbstractRequest);a.ACTIVEX_VERSIONS=["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],b.getResult=function(a){return a&&this._rawResponse?this._rawResponse:this._response},b.cancel=function(){this.canceled=!0,this._clean(),this._request.abort()},b.load=function(){if(null==this._request)return void this._handleError();null!=this._request.addEventListener?(this._request.addEventListener("loadstart",this._handleLoadStartProxy,!1),this._request.addEventListener("progress",this._handleProgressProxy,!1),this._request.addEventListener("abort",this._handleAbortProxy,!1),this._request.addEventListener("error",this._handleErrorProxy,!1),this._request.addEventListener("timeout",this._handleTimeoutProxy,!1),this._request.addEventListener("load",this._handleLoadProxy,!1),this._request.addEventListener("readystatechange",this._handleReadyStateChangeProxy,!1)):(this._request.onloadstart=this._handleLoadStartProxy,this._request.onprogress=this._handleProgressProxy,this._request.onabort=this._handleAbortProxy,this._request.onerror=this._handleErrorProxy,this._request.ontimeout=this._handleTimeoutProxy,this._request.onload=this._handleLoadProxy,this._request.onreadystatechange=this._handleReadyStateChangeProxy),1==this._xhrLevel&&(this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout));try{this._item.values&&this._item.method!=createjs.AbstractLoader.GET?this._item.method==createjs.AbstractLoader.POST&&this._request.send(createjs.RequestUtils.formatQueryString(this._item.values)):this._request.send()}catch(a){this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND",null,a))}},b.setResponseType=function(a){"blob"===a&&(a=window.URL?"blob":"arraybuffer",this._responseType=a),this._request.responseType=a},b.getAllResponseHeaders=function(){return this._request.getAllResponseHeaders instanceof Function?this._request.getAllResponseHeaders():null},b.getResponseHeader=function(a){return this._request.getResponseHeader instanceof Function?this._request.getResponseHeader(a):null},b._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},b._handleLoadStart=function(a){clearTimeout(this._loadTimeout),this.dispatchEvent("loadstart")},b._handleAbort=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("XHR_ABORTED",null,a))},b._handleError=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent(a.message))},b._handleReadyStateChange=function(a){4==this._request.readyState&&this._handleLoad()},b._handleLoad=function(a){if(!this.loaded){this.loaded=!0;var b=this._checkError();if(b)return void this._handleError(b);if(this._response=this._getResponse(),"arraybuffer"===this._responseType)try{this._response=new Blob([this._response])}catch(c){if(window.BlobBuilder=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,"TypeError"===c.name&&window.BlobBuilder){var d=new BlobBuilder;d.append(this._response),this._response=d.getBlob()}}this._clean(),this.dispatchEvent(new createjs.Event("complete"))}},b._handleTimeout=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT",null,a))},b._checkError=function(){var a=parseInt(this._request.status);switch(a){case 404:case 0:return new Error(a)}return null},b._getResponse=function(){if(null!=this._response)return this._response;if(null!=this._request.response)return this._request.response;try{if(null!=this._request.responseText)return this._request.responseText}catch(a){}try{if(null!=this._request.responseXML)return this._request.responseXML}catch(a){}return null},b._createXHR=function(a){var b=createjs.RequestUtils.isCrossDomain(a),c={},d=null;if(window.XMLHttpRequest)d=new XMLHttpRequest,b&&void 0===d.withCredentials&&window.XDomainRequest&&(d=new XDomainRequest);else{for(var e=0,f=s.ACTIVEX_VERSIONS.length;f>e;e++){var g=s.ACTIVEX_VERSIONS[e];try{d=new ActiveXObject(g);break}catch(h){}}if(null==d)return!1}null==a.mimeType&&createjs.RequestUtils.isText(a.type)&&(a.mimeType="text/plain; charset=utf-8"),a.mimeType&&d.overrideMimeType&&d.overrideMimeType(a.mimeType),this._xhrLevel="string"==typeof d.responseType?2:1;var i=null;if(i=a.method==createjs.AbstractLoader.GET?createjs.RequestUtils.buildPath(a.src,a.values):a.src,d.open(a.method||createjs.AbstractLoader.GET,i,!0),b&&d instanceof XMLHttpRequest&&1==this._xhrLevel&&(c.Origin=location.origin),a.values&&a.method==createjs.AbstractLoader.POST&&(c["Content-Type"]="application/x-www-form-urlencoded"),b||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest"),a.headers)for(var j in a.headers)c[j]=a.headers[j];for(j in c)d.setRequestHeader(j,c[j]);return d instanceof XMLHttpRequest&&void 0!==a.withCredentials&&(d.withCredentials=a.withCredentials),this._request=d,!0},b._clean=function(){clearTimeout(this._loadTimeout),null!=this._request.removeEventListener?(this._request.removeEventListener("loadstart",this._handleLoadStartProxy),this._request.removeEventListener("progress",this._handleProgressProxy),this._request.removeEventListener("abort",this._handleAbortProxy),this._request.removeEventListener("error",this._handleErrorProxy),this._request.removeEventListener("timeout",this._handleTimeoutProxy),this._request.removeEventListener("load",this._handleLoadProxy),this._request.removeEventListener("readystatechange",this._handleReadyStateChangeProxy)):(this._request.onloadstart=null,this._request.onprogress=null,this._request.onabort=null,this._request.onerror=null,this._request.ontimeout=null,this._request.onload=null,this._request.onreadystatechange=null)},b.toString=function(){return"[PreloadJS XHRRequest]"},createjs.XHRRequest=createjs.promote(a,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.AbstractLoader_constructor(),this._plugins=[],this._typeCallbacks={},this._extensionCallbacks={},this.next=null,this.maintainScriptOrder=!0,this.stopOnError=!1,this._maxConnections=1,this._availableLoaders=[createjs.ImageLoader,createjs.JavaScriptLoader,createjs.CSSLoader,createjs.JSONLoader,createjs.JSONPLoader,createjs.SoundLoader,createjs.ManifestLoader,createjs.SpriteSheetLoader,createjs.XMLLoader,createjs.SVGLoader,createjs.BinaryLoader,createjs.VideoLoader,createjs.TextLoader],this._defaultLoaderLength=this._availableLoaders.length,this.init(a,b,c)}var b=createjs.extend(a,createjs.AbstractLoader),c=a;b.init=function(a,b,c){this.useXHR=!0,this.preferXHR=!0,this._preferXHR=!0,this.setPreferXHR(a),this._paused=!1,this._basePath=b,this._crossOrigin=c,this._loadStartWasDispatched=!1,this._currentlyLoadingScript=null,this._currentLoads=[],this._loadQueue=[],this._loadQueueBackup=[],this._loadItemsById={},this._loadItemsBySrc={},this._loadedResults={},this._loadedRawResults={},this._numItems=0,this._numItemsLoaded=0,this._scriptOrder=[],this._loadedScripts=[],this._lastProgress=NaN},c.loadTimeout=8e3,c.LOAD_TIMEOUT=0,c.BINARY=createjs.AbstractLoader.BINARY,c.CSS=createjs.AbstractLoader.CSS,c.IMAGE=createjs.AbstractLoader.IMAGE,c.JAVASCRIPT=createjs.AbstractLoader.JAVASCRIPT,c.JSON=createjs.AbstractLoader.JSON,c.JSONP=createjs.AbstractLoader.JSONP,c.MANIFEST=createjs.AbstractLoader.MANIFEST,c.SOUND=createjs.AbstractLoader.SOUND,c.VIDEO=createjs.AbstractLoader.VIDEO,c.SVG=createjs.AbstractLoader.SVG,c.TEXT=createjs.AbstractLoader.TEXT,c.XML=createjs.AbstractLoader.XML,c.POST=createjs.AbstractLoader.POST,c.GET=createjs.AbstractLoader.GET,b.registerLoader=function(a){if(!a||!a.canLoadItem)throw new Error("loader is of an incorrect type.");if(-1!=this._availableLoaders.indexOf(a))throw new Error("loader already exists.");this._availableLoaders.unshift(a)},b.unregisterLoader=function(a){var b=this._availableLoaders.indexOf(a);-1!=b&&b0)return;var c=!1;if(b){for(;b.length;){var d=b.pop(),e=this.getResult(d);for(f=this._loadQueue.length-1;f>=0;f--)if(g=this._loadQueue[f].getItem(),g.id==d||g.src==d){this._loadQueue.splice(f,1)[0].cancel();break}for(f=this._loadQueueBackup.length-1;f>=0;f--)if(g=this._loadQueueBackup[f].getItem(),g.id==d||g.src==d){this._loadQueueBackup.splice(f,1)[0].cancel();break}if(e)this._disposeItem(this.getItem(d));else for(var f=this._currentLoads.length-1;f>=0;f--){var g=this._currentLoads[f].getItem();if(g.id==d||g.src==d){this._currentLoads.splice(f,1)[0].cancel(),c=!0;break}}}c&&this._loadNext()}else{this.close();for(var h in this._loadItemsById)this._disposeItem(this._loadItemsById[h]);this.init(this.preferXHR,this._basePath)}},b.reset=function(){this.close();for(var a in this._loadItemsById)this._disposeItem(this._loadItemsById[a]);for(var b=[],c=0,d=this._loadQueueBackup.length;d>c;c++)b.push(this._loadQueueBackup[c].getItem());this.loadManifest(b,!1)},b.installPlugin=function(a){if(null!=a&&null!=a.getPreloadHandlers){this._plugins.push(a);var b=a.getPreloadHandlers();if(b.scope=a,null!=b.types)for(var c=0,d=b.types.length;d>c;c++)this._typeCallbacks[b.types[c]]=b;if(null!=b.extensions)for(c=0,d=b.extensions.length;d>c;c++)this._extensionCallbacks[b.extensions[c]]=b}},b.setMaxConnections=function(a){this._maxConnections=a,!this._paused&&this._loadQueue.length>0&&this._loadNext()},b.loadFile=function(a,b,c){if(null==a){var d=new createjs.ErrorEvent("PRELOAD_NO_FILE");return void this._sendError(d)}this._addItem(a,null,c),b!==!1?this.setPaused(!1):this.setPaused(!0)},b.loadManifest=function(a,b,d){var e=null,f=null;if(Array.isArray(a)){if(0==a.length){var g=new createjs.ErrorEvent("PRELOAD_MANIFEST_EMPTY");return void this._sendError(g)}e=a}else if("string"==typeof a)e=[{src:a,type:c.MANIFEST}];else{if("object"!=typeof a){var g=new createjs.ErrorEvent("PRELOAD_MANIFEST_NULL");return void this._sendError(g)}if(void 0!==a.src){if(null==a.type)a.type=c.MANIFEST;else if(a.type!=c.MANIFEST){var g=new createjs.ErrorEvent("PRELOAD_MANIFEST_TYPE");this._sendError(g)}e=[a]}else void 0!==a.manifest&&(e=a.manifest,f=a.path)}for(var h=0,i=e.length;i>h;h++)this._addItem(e[h],f,d);b!==!1?this.setPaused(!1):this.setPaused(!0)},b.load=function(){this.setPaused(!1)},b.getItem=function(a){return this._loadItemsById[a]||this._loadItemsBySrc[a]},b.getResult=function(a,b){var c=this._loadItemsById[a]||this._loadItemsBySrc[a];if(null==c)return null;var d=c.id;return b&&this._loadedRawResults[d]?this._loadedRawResults[d]:this._loadedResults[d]},b.getItems=function(a){var b=[];for(var c in this._loadItemsById){var d=this._loadItemsById[c],e=this.getResult(c);(a!==!0||null!=e)&&b.push({item:d,result:e,rawResult:this.getResult(c,!0)})}return b},b.setPaused=function(a){this._paused=a,this._paused||this._loadNext()},b.close=function(){for(;this._currentLoads.length;)this._currentLoads.pop().cancel();this._scriptOrder.length=0,this._loadedScripts.length=0,this.loadStartWasDispatched=!1,this._itemCount=0,this._lastProgress=NaN},b._addItem=function(a,b,c){var d=this._createLoadItem(a,b,c);if(null!=d){var e=this._createLoader(d);null!=e&&("plugins"in e&&(e.plugins=this._plugins),d._loader=e,this._loadQueue.push(e),this._loadQueueBackup.push(e),this._numItems++,this._updateProgress(),(this.maintainScriptOrder&&d.type==createjs.LoadQueue.JAVASCRIPT||d.maintainOrder===!0)&&(this._scriptOrder.push(d),this._loadedScripts.push(null)))}},b._createLoadItem=function(a,b,c){var d=createjs.LoadItem.create(a);if(null==d)return null;var e="",f=c||this._basePath;if(d.src instanceof Object){if(!d.type)return null;if(b){e=b;var g=createjs.RequestUtils.parseURI(b);null==f||g.absolute||g.relative||(e=f+e)}else null!=f&&(e=f)}else{var h=createjs.RequestUtils.parseURI(d.src);h.extension&&(d.ext=h.extension),null==d.type&&(d.type=createjs.RequestUtils.getTypeByExtension(d.ext));var i=d.src;if(!h.absolute&&!h.relative)if(b){e=b;var g=createjs.RequestUtils.parseURI(b);i=b+i,null==f||g.absolute||g.relative||(e=f+e)}else null!=f&&(e=f);d.src=e+d.src}d.path=e,(void 0===d.id||null===d.id||""===d.id)&&(d.id=i);var j=this._typeCallbacks[d.type]||this._extensionCallbacks[d.ext];if(j){var k=j.callback.call(j.scope,d,this);if(k===!1)return null;k===!0||null!=k&&(d._loader=k),h=createjs.RequestUtils.parseURI(d.src),null!=h.extension&&(d.ext=h.extension)}return this._loadItemsById[d.id]=d,this._loadItemsBySrc[d.src]=d,null==d.crossOrigin&&(d.crossOrigin=this._crossOrigin),d},b._createLoader=function(a){if(null!=a._loader)return a._loader;for(var b=this.preferXHR,c=0;c=this._maxConnections);a++){var b=this._loadQueue[a];this._canStartLoad(b)&&(this._loadQueue.splice(a,1),a--,this._loadItem(b))}}},b._loadItem=function(a){a.on("fileload",this._handleFileLoad,this),a.on("progress",this._handleProgress,this),a.on("complete",this._handleFileComplete,this),a.on("error",this._handleError,this),a.on("fileerror",this._handleFileError,this),this._currentLoads.push(a),this._sendFileStart(a.getItem()),a.load()},b._handleFileLoad=function(a){a.target=null,this.dispatchEvent(a)},b._handleFileError=function(a){var b=new createjs.ErrorEvent("FILE_LOAD_ERROR",null,a.item);this._sendError(b)},b._handleError=function(a){var b=a.target;this._numItemsLoaded++,this._finishOrderedItem(b,!0),this._updateProgress();var c=new createjs.ErrorEvent("FILE_LOAD_ERROR",null,b.getItem());this._sendError(c),this.stopOnError?this.setPaused(!0):(this._removeLoadItem(b),this._cleanLoadItem(b),this._loadNext())},b._handleFileComplete=function(a){var b=a.target,c=b.getItem(),d=b.getResult();this._loadedResults[c.id]=d;var e=b.getResult(!0);null!=e&&e!==d&&(this._loadedRawResults[c.id]=e),this._saveLoadedItems(b),this._removeLoadItem(b),this._finishOrderedItem(b)||this._processFinishedLoad(c,b),this._cleanLoadItem(b)},b._saveLoadedItems=function(a){var b=a.getLoadedItems();if(null!==b)for(var c=0;cb;b++){var c=this._loadedScripts[b];if(null===c)break;if(c!==!0){var d=this._loadedResults[c.id];c.type==createjs.LoadQueue.JAVASCRIPT&&createjs.DomUtils.appendToHead(d);var e=c._loader;this._processFinishedLoad(c,e),this._loadedScripts[b]=!0}}},b._processFinishedLoad=function(a,b){if(this._numItemsLoaded++,!this.maintainScriptOrder&&a.type==createjs.LoadQueue.JAVASCRIPT){var c=b.getTag();createjs.DomUtils.appendToHead(c)}this._updateProgress(),this._sendFileComplete(a,b),this._loadNext()},b._canStartLoad=function(a){if(!this.maintainScriptOrder||a.preferXHR)return!0;var b=a.getItem();if(b.type!=createjs.LoadQueue.JAVASCRIPT)return!0;if(this._currentlyLoadingScript)return!1;for(var c=this._scriptOrder.indexOf(b),d=0;c>d;){var e=this._loadedScripts[d];if(null==e)return!1;d++}return this._currentlyLoadingScript=!0,!0},b._removeLoadItem=function(a){for(var b=this._currentLoads.length,c=0;b>c;c++)if(this._currentLoads[c]==a){this._currentLoads.splice(c,1);break}},b._cleanLoadItem=function(a){var b=a.getItem();b&&delete b._loader},b._handleProgress=function(a){var b=a.target;this._sendFileProgress(b.getItem(),b.progress),this._updateProgress()},b._updateProgress=function(){var a=this._numItemsLoaded/this._numItems,b=this._numItems-this._numItemsLoaded;if(b>0){for(var c=0,d=0,e=this._currentLoads.length;e>d;d++)c+=this._currentLoads[d].progress;a+=c/b*(b/this._numItems)}this._lastProgress!=a&&(this._sendProgress(a),this._lastProgress=a)},b._disposeItem=function(a){delete this._loadedResults[a.id],delete this._loadedRawResults[a.id],delete this._loadItemsById[a.id],delete this._loadItemsBySrc[a.src]},b._sendFileProgress=function(a,b){if(!this._isCanceled()&&!this._paused&&this.hasEventListener("fileprogress")){var c=new createjs.Event("fileprogress");c.progress=b,c.loaded=b,c.total=1,c.item=a,this.dispatchEvent(c)}},b._sendFileComplete=function(a,b){ +if(!this._isCanceled()&&!this._paused){var c=new createjs.Event("fileload");c.loader=b,c.item=a,c.result=this._loadedResults[a.id],c.rawResult=this._loadedRawResults[a.id],a.completeHandler&&a.completeHandler(c),this.hasEventListener("fileload")&&this.dispatchEvent(c)}},b._sendFileStart=function(a){var b=new createjs.Event("filestart");b.item=a,this.hasEventListener("filestart")&&this.dispatchEvent(b)},b.toString=function(){return"[PreloadJS LoadQueue]"},createjs.LoadQueue=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractLoader_constructor(a,!0,createjs.AbstractLoader.TEXT)}var b=(createjs.extend(a,createjs.AbstractLoader),a);b.canLoadItem=function(a){return a.type==createjs.AbstractLoader.TEXT},createjs.TextLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractLoader_constructor(a,!0,createjs.AbstractLoader.BINARY),this.on("initialize",this._updateXHR,this)}var b=createjs.extend(a,createjs.AbstractLoader),c=a;c.canLoadItem=function(a){return a.type==createjs.AbstractLoader.BINARY},b._updateXHR=function(a){a.loader.setResponseType("arraybuffer")},createjs.BinaryLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b){this.AbstractLoader_constructor(a,b,createjs.AbstractLoader.CSS),this.resultFormatter=this._formatResult,this._tagSrcAttribute="href",b?this._tag=document.createElement("style"):this._tag=document.createElement("link"),this._tag.rel="stylesheet",this._tag.type="text/css"}var b=createjs.extend(a,createjs.AbstractLoader),c=a;c.canLoadItem=function(a){return a.type==createjs.AbstractLoader.CSS},b._formatResult=function(a){if(this._preferXHR){var b=a.getTag();if(b.styleSheet)b.styleSheet.cssText=a.getResult(!0);else{var c=document.createTextNode(a.getResult(!0));b.appendChild(c)}}else b=this._tag;return createjs.DomUtils.appendToHead(b),b},createjs.CSSLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b){this.AbstractLoader_constructor(a,b,createjs.AbstractLoader.IMAGE),this.resultFormatter=this._formatResult,this._tagSrcAttribute="src",createjs.RequestUtils.isImageTag(a)?this._tag=a:createjs.RequestUtils.isImageTag(a.src)?this._tag=a.src:createjs.RequestUtils.isImageTag(a.tag)&&(this._tag=a.tag),null!=this._tag?this._preferXHR=!1:this._tag=document.createElement("img"),this.on("initialize",this._updateXHR,this)}var b=createjs.extend(a,createjs.AbstractLoader),c=a;c.canLoadItem=function(a){return a.type==createjs.AbstractLoader.IMAGE},b.load=function(){if(""!=this._tag.src&&this._tag.complete)return void this._sendComplete();var a=this._item.crossOrigin;1==a&&(a="Anonymous"),null==a||createjs.RequestUtils.isLocal(this._item.src)||(this._tag.crossOrigin=a),this.AbstractLoader_load()},b._updateXHR=function(a){a.loader.mimeType="text/plain; charset=x-user-defined-binary",a.loader.setResponseType&&a.loader.setResponseType("blob")},b._formatResult=function(a){return this._formatImage},b._formatImage=function(a,b){var c=this._tag,d=window.URL||window.webkitURL;if(this._preferXHR)if(d){var e=d.createObjectURL(this.getResult(!0));c.src=e,c.addEventListener("load",this._cleanUpURL,!1),c.addEventListener("error",this._cleanUpURL,!1)}else c.src=this._item.src;else;c.complete?a(c):(c.onload=createjs.proxy(function(){a(this._tag)},this),c.onerror=createjs.proxy(function(){b(_this._tag)},this))},b._cleanUpURL=function(a){var b=window.URL||window.webkitURL;b.revokeObjectURL(a.target.src)},createjs.ImageLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b){this.AbstractLoader_constructor(a,b,createjs.AbstractLoader.JAVASCRIPT),this.resultFormatter=this._formatResult,this._tagSrcAttribute="src",this.setTag(document.createElement("script"))}var b=createjs.extend(a,createjs.AbstractLoader),c=a;c.canLoadItem=function(a){return a.type==createjs.AbstractLoader.JAVASCRIPT},b._formatResult=function(a){var b=a.getTag();return this._preferXHR&&(b.text=a.getResult(!0)),b},createjs.JavaScriptLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractLoader_constructor(a,!0,createjs.AbstractLoader.JSON),this.resultFormatter=this._formatResult}var b=createjs.extend(a,createjs.AbstractLoader),c=a;c.canLoadItem=function(a){return a.type==createjs.AbstractLoader.JSON},b._formatResult=function(a){var b=null;try{b=createjs.DataUtils.parseJSON(a.getResult(!0))}catch(c){var d=new createjs.ErrorEvent("JSON_FORMAT",null,c);return this._sendError(d),c}return b},createjs.JSONLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractLoader_constructor(a,!1,createjs.AbstractLoader.JSONP),this.setTag(document.createElement("script")),this.getTag().type="text/javascript"}var b=createjs.extend(a,createjs.AbstractLoader),c=a;c.canLoadItem=function(a){return a.type==createjs.AbstractLoader.JSONP},b.cancel=function(){this.AbstractLoader_cancel(),this._dispose()},b.load=function(){if(null==this._item.callback)throw new Error("callback is required for loading JSONP requests.");if(null!=window[this._item.callback])throw new Error("JSONP callback '"+this._item.callback+"' already exists on window. You need to specify a different callback or re-name the current one.");window[this._item.callback]=createjs.proxy(this._handleLoad,this),window.document.body.appendChild(this._tag),this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout),this._tag.src=this._item.src},b._handleLoad=function(a){this._result=this._rawResult=a,this._sendComplete(),this._dispose()},b._handleTimeout=function(){this._dispose(),this.dispatchEvent(new createjs.ErrorEvent("timeout"))},b._dispose=function(){window.document.body.removeChild(this._tag),delete window[this._item.callback],clearTimeout(this._loadTimeout)},createjs.JSONPLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractLoader_constructor(a,null,createjs.AbstractLoader.MANIFEST),this.plugins=null,this._manifestQueue=null}var b=createjs.extend(a,createjs.AbstractLoader),c=a;c.MANIFEST_PROGRESS=.25,c.canLoadItem=function(a){return a.type==createjs.AbstractLoader.MANIFEST},b.load=function(){this.AbstractLoader_load()},b._createRequest=function(){var a=this._item.callback;null!=a?this._request=new createjs.JSONPLoader(this._item):this._request=new createjs.JSONLoader(this._item)},b.handleEvent=function(a){switch(a.type){case"complete":return this._rawResult=a.target.getResult(!0),this._result=a.target.getResult(),this._sendProgress(c.MANIFEST_PROGRESS),void this._loadManifest(this._result);case"progress":return a.loaded*=c.MANIFEST_PROGRESS,this.progress=a.loaded/a.total,(isNaN(this.progress)||this.progress==1/0)&&(this.progress=0),void this._sendProgress(a)}this.AbstractLoader_handleEvent(a)},b.destroy=function(){this.AbstractLoader_destroy(),this._manifestQueue.close()},b._loadManifest=function(a){if(a&&a.manifest){var b=this._manifestQueue=new createjs.LoadQueue;b.on("fileload",this._handleManifestFileLoad,this),b.on("progress",this._handleManifestProgress,this),b.on("complete",this._handleManifestComplete,this,!0),b.on("error",this._handleManifestError,this,!0);for(var c=0,d=this.plugins.length;d>c;c++)b.installPlugin(this.plugins[c]);b.loadManifest(a)}else this._sendComplete()},b._handleManifestFileLoad=function(a){a.target=null,this.dispatchEvent(a)},b._handleManifestComplete=function(a){this._loadedItems=this._manifestQueue.getItems(!0),this._sendComplete()},b._handleManifestProgress=function(a){this.progress=a.progress*(1-c.MANIFEST_PROGRESS)+c.MANIFEST_PROGRESS,this._sendProgress(this.progress)},b._handleManifestError=function(a){var b=new createjs.Event("fileerror");b.item=a.data,this.dispatchEvent(b)},createjs.ManifestLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b){this.AbstractMediaLoader_constructor(a,b,createjs.AbstractLoader.SOUND),createjs.RequestUtils.isAudioTag(a)?this._tag=a:createjs.RequestUtils.isAudioTag(a.src)?this._tag=a:createjs.RequestUtils.isAudioTag(a.tag)&&(this._tag=createjs.RequestUtils.isAudioTag(a)?a:a.src),null!=this._tag&&(this._preferXHR=!1)}var b=createjs.extend(a,createjs.AbstractMediaLoader),c=a;c.canLoadItem=function(a){return a.type==createjs.AbstractLoader.SOUND},b._createTag=function(a){var b=document.createElement("audio");return b.autoplay=!1,b.preload="none",b.src=a,b},createjs.SoundLoader=createjs.promote(a,"AbstractMediaLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b){this.AbstractMediaLoader_constructor(a,b,createjs.AbstractLoader.VIDEO),createjs.RequestUtils.isVideoTag(a)||createjs.RequestUtils.isVideoTag(a.src)?(this.setTag(createjs.RequestUtils.isVideoTag(a)?a:a.src),this._preferXHR=!1):this.setTag(this._createTag())}var b=createjs.extend(a,createjs.AbstractMediaLoader),c=a;b._createTag=function(){return document.createElement("video")},c.canLoadItem=function(a){return a.type==createjs.AbstractLoader.VIDEO},createjs.VideoLoader=createjs.promote(a,"AbstractMediaLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b){this.AbstractLoader_constructor(a,b,createjs.AbstractLoader.SPRITESHEET),this._manifestQueue=null}var b=createjs.extend(a,createjs.AbstractLoader),c=a;c.SPRITESHEET_PROGRESS=.25,c.canLoadItem=function(a){return a.type==createjs.AbstractLoader.SPRITESHEET},b.destroy=function(){this.AbstractLoader_destroy,this._manifestQueue.close()},b._createRequest=function(){var a=this._item.callback;null!=a?this._request=new createjs.JSONPLoader(this._item):this._request=new createjs.JSONLoader(this._item)},b.handleEvent=function(a){switch(a.type){case"complete":return this._rawResult=a.target.getResult(!0),this._result=a.target.getResult(),this._sendProgress(c.SPRITESHEET_PROGRESS),void this._loadManifest(this._result);case"progress":return a.loaded*=c.SPRITESHEET_PROGRESS,this.progress=a.loaded/a.total,(isNaN(this.progress)||this.progress==1/0)&&(this.progress=0),void this._sendProgress(a)}this.AbstractLoader_handleEvent(a)},b._loadManifest=function(a){if(a&&a.images){var b=this._manifestQueue=new createjs.LoadQueue(this._preferXHR,this._item.path,this._item.crossOrigin);b.on("complete",this._handleManifestComplete,this,!0),b.on("fileload",this._handleManifestFileLoad,this),b.on("progress",this._handleManifestProgress,this),b.on("error",this._handleManifestError,this,!0),b.loadManifest(a.images)}},b._handleManifestFileLoad=function(a){var b=a.result;if(null!=b){var c=this.getResult().images,d=c.indexOf(a.item.src);c[d]=b}},b._handleManifestComplete=function(a){this._result=new createjs.SpriteSheet(this._result),this._loadedItems=this._manifestQueue.getItems(!0),this._sendComplete()},b._handleManifestProgress=function(a){this.progress=a.progress*(1-c.SPRITESHEET_PROGRESS)+c.SPRITESHEET_PROGRESS,this._sendProgress(this.progress)},b._handleManifestError=function(a){var b=new createjs.Event("fileerror");b.item=a.data,this.dispatchEvent(b)},createjs.SpriteSheetLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b){this.AbstractLoader_constructor(a,b,createjs.AbstractLoader.SVG),this.resultFormatter=this._formatResult,this._tagSrcAttribute="data",b?this.setTag(document.createElement("svg")):(this.setTag(document.createElement("object")),this.getTag().type="image/svg+xml")}var b=createjs.extend(a,createjs.AbstractLoader),c=a;c.canLoadItem=function(a){return a.type==createjs.AbstractLoader.SVG},b._formatResult=function(a){var b=createjs.DataUtils.parseXML(a.getResult(!0),"text/xml"),c=a.getTag();return!this._preferXHR&&document.body.contains(c)&&document.body.removeChild(c),null!=b.documentElement?(c.appendChild(b.documentElement),c.style.visibility="visible",c):b},createjs.SVGLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractLoader_constructor(a,!0,createjs.AbstractLoader.XML),this.resultFormatter=this._formatResult}var b=createjs.extend(a,createjs.AbstractLoader),c=a;c.canLoadItem=function(a){return a.type==createjs.AbstractLoader.XML},b._formatResult=function(a){return createjs.DataUtils.parseXML(a.getResult(!0),"text/xml")},createjs.XMLLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){var a=createjs.SoundJS=createjs.SoundJS||{};a.version="0.6.2",a.buildDate="Thu, 26 Nov 2015 20:44:31 GMT"}(),this.createjs=this.createjs||{},createjs.indexOf=function(a,b){"use strict";for(var c=0,d=a.length;d>c;c++)if(b===a[c])return c;return-1},this.createjs=this.createjs||{},function(){"use strict";createjs.proxy=function(a,b){var c=Array.prototype.slice.call(arguments,2);return function(){return a.apply(b,Array.prototype.slice.call(arguments,0).concat(c))}}}(),this.createjs=this.createjs||{},function(){"use strict";function a(){throw"BrowserDetect cannot be instantiated"}var b=a.agent=window.navigator.userAgent;a.isWindowPhone=b.indexOf("IEMobile")>-1||b.indexOf("Windows Phone")>-1,a.isFirefox=b.indexOf("Firefox")>-1,a.isOpera=null!=window.opera,a.isChrome=b.indexOf("Chrome")>-1,a.isIOS=(b.indexOf("iPod")>-1||b.indexOf("iPhone")>-1||b.indexOf("iPad")>-1)&&!a.isWindowPhone,a.isAndroid=b.indexOf("Android")>-1&&!a.isWindowPhone,a.isBlackberry=b.indexOf("Blackberry")>-1,createjs.BrowserDetect=a}(),this.createjs=this.createjs||{},function(){"use strict";var a=function(){this.interrupt=null,this.delay=null,this.offset=null,this.loop=null,this.volume=null,this.pan=null,this.startTime=null,this.duration=null},b=a.prototype={},c=a;c.create=function(a){if(a instanceof c||a instanceof Object){var b=new createjs.PlayPropsConfig;return b.set(a),b}throw new Error("Type not recognized.")},b.set=function(a){for(var b in a)this[b]=a[b];return this},b.toString=function(){return"[PlayPropsConfig]"},createjs.PlayPropsConfig=c}(),this.createjs=this.createjs||{},function(){"use strict";function a(){throw"Sound cannot be instantiated"}function b(a,b){this.init(a,b)}var c=a;c.INTERRUPT_ANY="any",c.INTERRUPT_EARLY="early",c.INTERRUPT_LATE="late",c.INTERRUPT_NONE="none",c.PLAY_INITED="playInited",c.PLAY_SUCCEEDED="playSucceeded",c.PLAY_INTERRUPTED="playInterrupted",c.PLAY_FINISHED="playFinished",c.PLAY_FAILED="playFailed",c.SUPPORTED_EXTENSIONS=["mp3","ogg","opus","mpeg","wav","m4a","mp4","aiff","wma","mid"],c.EXTENSION_MAP={m4a:"mp4"},c.FILE_PATTERN=/^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([\/.]*?(?:[^?]+)?\/)?((?:[^\/?]+)\.(\w+))(?:\?(\S+)?)?$/,c.defaultInterruptBehavior=c.INTERRUPT_NONE,c.alternateExtensions=[],c.activePlugin=null,c._masterVolume=1,Object.defineProperty(c,"volume",{get:function(){return this._masterVolume},set:function(a){if(null==Number(a))return!1;if(a=Math.max(0,Math.min(1,a)),c._masterVolume=a,!this.activePlugin||!this.activePlugin.setVolume||!this.activePlugin.setVolume(a))for(var b=this._instances,d=0,e=b.length;e>d;d++)b[d].setMasterVolume(a)}}),c._masterMute=!1,Object.defineProperty(c,"muted",{get:function(){return this._masterMute},set:function(a){if(null==a)return!1;if(this._masterMute=a,!this.activePlugin||!this.activePlugin.setMute||!this.activePlugin.setMute(a))for(var b=this._instances,c=0,d=b.length;d>c;c++)b[c].setMasterMute(a);return!0}}),Object.defineProperty(c,"capabilities",{get:function(){return null==c.activePlugin?null:c.activePlugin._capabilities},set:function(a){return!1}}),c._pluginsRegistered=!1,c._lastID=0,c._instances=[],c._idHash={},c._preloadHash={},c._defaultPlayPropsHash={},c.addEventListener=null,c.removeEventListener=null,c.removeAllEventListeners=null,c.dispatchEvent=null,c.hasEventListener=null,c._listeners=null,createjs.EventDispatcher.initialize(c),c.getPreloadHandlers=function(){return{callback:createjs.proxy(c.initLoad,c),types:["sound"],extensions:c.SUPPORTED_EXTENSIONS}},c._handleLoadComplete=function(a){var b=a.target.getItem().src;if(c._preloadHash[b])for(var d=0,e=c._preloadHash[b].length;e>d;d++){var f=c._preloadHash[b][d];if(c._preloadHash[b][d]=!0,c.hasEventListener("fileload")){var a=new createjs.Event("fileload");a.src=f.src,a.id=f.id,a.data=f.data,a.sprite=f.sprite,c.dispatchEvent(a)}}},c._handleLoadError=function(a){var b=a.target.getItem().src;if(c._preloadHash[b])for(var d=0,e=c._preloadHash[b].length;e>d;d++){var f=c._preloadHash[b][d];if(c._preloadHash[b][d]=!1,c.hasEventListener("fileerror")){var a=new createjs.Event("fileerror");a.src=f.src,a.id=f.id,a.data=f.data,a.sprite=f.sprite,c.dispatchEvent(a)}}},c._registerPlugin=function(a){return a.isSupported()?(c.activePlugin=new a,!0):!1},c.registerPlugins=function(a){c._pluginsRegistered=!0;for(var b=0,d=a.length;d>b;b++)if(c._registerPlugin(a[b]))return!0;return!1},c.initializeDefaultPlugins=function(){return null!=c.activePlugin?!0:c._pluginsRegistered?!1:c.registerPlugins([createjs.WebAudioPlugin,createjs.HTMLAudioPlugin])?!0:!1},c.isReady=function(){return null!=c.activePlugin},c.getCapabilities=function(){return null==c.activePlugin?null:c.activePlugin._capabilities},c.getCapability=function(a){return null==c.activePlugin?null:c.activePlugin._capabilities[a]},c.initLoad=function(a){return c._registerSound(a)},c._registerSound=function(a){if(!c.initializeDefaultPlugins())return!1;var d;if(a.src instanceof Object?(d=c._parseSrc(a.src),d.src=a.path+d.src):d=c._parsePath(a.src),null==d)return!1;a.src=d.src,a.type="sound";var e=a.data,f=null;if(null!=e&&(isNaN(e.channels)?isNaN(e)||(f=parseInt(e)):f=parseInt(e.channels),e.audioSprite))for(var g,h=e.audioSprite.length;h--;)g=e.audioSprite[h],c._idHash[g.id]={src:a.src,startTime:parseInt(g.startTime),duration:parseInt(g.duration)},g.defaultPlayProps&&(c._defaultPlayPropsHash[g.id]=createjs.PlayPropsConfig.create(g.defaultPlayProps));null!=a.id&&(c._idHash[a.id]={src:a.src});var i=c.activePlugin.register(a);return b.create(a.src,f),null!=e&&isNaN(e)?a.data.channels=f||b.maxPerChannel():a.data=f||b.maxPerChannel(),i.type&&(a.type=i.type),a.defaultPlayProps&&(c._defaultPlayPropsHash[a.src]=createjs.PlayPropsConfig.create(a.defaultPlayProps)),i},c.registerSound=function(a,b,d,e,f){var g={src:a,id:b,data:d,defaultPlayProps:f};a instanceof Object&&a.src&&(e=b,g=a),g=createjs.LoadItem.create(g),g.path=e,null==e||g.src instanceof Object||(g.src=e+a);var h=c._registerSound(g);if(!h)return!1;if(c._preloadHash[g.src]||(c._preloadHash[g.src]=[]),c._preloadHash[g.src].push(g),1==c._preloadHash[g.src].length)h.on("complete",createjs.proxy(this._handleLoadComplete,this)),h.on("error",createjs.proxy(this._handleLoadError,this)),c.activePlugin.preload(h);else if(1==c._preloadHash[g.src][0])return!0;return g},c.registerSounds=function(a,b){var c=[];a.path&&(b?b+=a.path:b=a.path,a=a.manifest);for(var d=0,e=a.length;e>d;d++)c[d]=createjs.Sound.registerSound(a[d].src,a[d].id,a[d].data,b,a[d].defaultPlayProps);return c},c.removeSound=function(a,d){if(null==c.activePlugin)return!1;a instanceof Object&&a.src&&(a=a.src);var e;if(a instanceof Object?e=c._parseSrc(a):(a=c._getSrcById(a).src,e=c._parsePath(a)),null==e)return!1;a=e.src,null!=d&&(a=d+a);for(var f in c._idHash)c._idHash[f].src==a&&delete c._idHash[f];return b.removeSrc(a),delete c._preloadHash[a],c.activePlugin.removeSound(a),!0},c.removeSounds=function(a,b){var c=[];a.path&&(b?b+=a.path:b=a.path,a=a.manifest);for(var d=0,e=a.length;e>d;d++)c[d]=createjs.Sound.removeSound(a[d].src,b);return c},c.removeAllSounds=function(){c._idHash={},c._preloadHash={},b.removeAll(),c.activePlugin&&c.activePlugin.removeAllSounds()},c.loadComplete=function(a){if(!c.isReady())return!1;var b=c._parsePath(a);return a=b?c._getSrcById(b.src).src:c._getSrcById(a).src,void 0==c._preloadHash[a]?!1:1==c._preloadHash[a][0]},c._parsePath=function(a){"string"!=typeof a&&(a=a.toString());var b=a.match(c.FILE_PATTERN);if(null==b)return!1;for(var d=b[4],e=b[5],f=c.capabilities,g=0;!f[e];)if(e=c.alternateExtensions[g++],g>c.alternateExtensions.length)return null;a=a.replace("."+b[5],"."+e);var h={name:d,src:a,extension:e};return h},c._parseSrc=function(a){var b={name:void 0,src:void 0,extension:void 0},d=c.capabilities;for(var e in a)if(a.hasOwnProperty(e)&&d[e]){b.src=a[e],b.extension=e;break}if(!b.src)return!1;var f=b.src.lastIndexOf("/");return-1!=f?b.name=b.src.slice(f+1):b.name=b.src,b},c.play=function(a,b,d,e,f,g,h,i,j){var k;k=b instanceof Object||b instanceof createjs.PlayPropsConfig?createjs.PlayPropsConfig.create(b):createjs.PlayPropsConfig.create({interrupt:b,delay:d,offset:e,loop:f,volume:g,pan:h,startTime:i,duration:j});var l=c.createInstance(a,k.startTime,k.duration),m=c._playInstance(l,k);return m||l._playFailed(),l},c.createInstance=function(a,d,e){if(!c.initializeDefaultPlugins())return new createjs.DefaultSoundInstance(a,d,e);var f=c._defaultPlayPropsHash[a];a=c._getSrcById(a);var g=c._parsePath(a.src),h=null;return null!=g&&null!=g.src?(b.create(g.src),null==d&&(d=a.startTime),h=c.activePlugin.create(g.src,d,e||a.duration),f=f||c._defaultPlayPropsHash[g.src],f&&h.applyPlayProps(f)):h=new createjs.DefaultSoundInstance(a,d,e),h.uniqueId=c._lastID++,h},c.stop=function(){for(var a=this._instances,b=a.length;b--;)a[b].stop()},c.setVolume=function(a){if(null==Number(a))return!1;if(a=Math.max(0,Math.min(1,a)),c._masterVolume=a,!this.activePlugin||!this.activePlugin.setVolume||!this.activePlugin.setVolume(a))for(var b=this._instances,d=0,e=b.length;e>d;d++)b[d].setMasterVolume(a)},c.getVolume=function(){return this._masterVolume},c.setMute=function(a){if(null==a)return!1;if(this._masterMute=a,!this.activePlugin||!this.activePlugin.setMute||!this.activePlugin.setMute(a))for(var b=this._instances,c=0,d=b.length;d>c;c++)b[c].setMasterMute(a);return!0},c.getMute=function(){return this._masterMute},c.setDefaultPlayProps=function(a,b){a=c._getSrcById(a),c._defaultPlayPropsHash[c._parsePath(a.src).src]=createjs.PlayPropsConfig.create(b)},c.getDefaultPlayProps=function(a){return a=c._getSrcById(a),c._defaultPlayPropsHash[c._parsePath(a.src).src]},c._playInstance=function(a,b){var d=c._defaultPlayPropsHash[a.src]||{};if(null==b.interrupt&&(b.interrupt=d.interrupt||c.defaultInterruptBehavior),null==b.delay&&(b.delay=d.delay||0),null==b.offset&&(b.offset=a.getPosition()),null==b.loop&&(b.loop=a.loop),null==b.volume&&(b.volume=a.volume),null==b.pan&&(b.pan=a.pan),0==b.delay){var e=c._beginPlaying(a,b);if(!e)return!1}else{var f=setTimeout(function(){c._beginPlaying(a,b)},b.delay);a.delayTimeoutId=f}return this._instances.push(a),!0},c._beginPlaying=function(a,c){if(!b.add(a,c.interrupt))return!1;var d=a._beginPlaying(c);if(!d){var e=createjs.indexOf(this._instances,a);return e>-1&&this._instances.splice(e,1),!1}return!0},c._getSrcById=function(a){return c._idHash[a]||{src:a}},c._playFinished=function(a){b.remove(a);var c=createjs.indexOf(this._instances,a);c>-1&&this._instances.splice(c,1)},createjs.Sound=a,b.channels={},b.create=function(a,c){var d=b.get(a);return null==d?(b.channels[a]=new b(a,c),!0):!1},b.removeSrc=function(a){var c=b.get(a);return null==c?!1:(c._removeAll(),delete b.channels[a],!0)},b.removeAll=function(){for(var a in b.channels)b.channels[a]._removeAll();b.channels={}},b.add=function(a,c){var d=b.get(a.src);return null==d?!1:d._add(a,c)},b.remove=function(a){var c=b.get(a.src);return null==c?!1:(c._remove(a),!0)},b.maxPerChannel=function(){return d.maxDefault},b.get=function(a){return b.channels[a]};var d=b.prototype;d.constructor=b,d.src=null,d.max=null,d.maxDefault=100,d.length=0,d.init=function(a,b){this.src=a,this.max=b||this.maxDefault,-1==this.max&&(this.max=this.maxDefault),this._instances=[]},d._get=function(a){return this._instances[a]},d._add=function(a,b){return this._getSlot(b,a)?(this._instances.push(a),this.length++,!0):!1},d._remove=function(a){var b=createjs.indexOf(this._instances,a);return-1==b?!1:(this._instances.splice(b,1),this.length--,!0)},d._removeAll=function(){for(var a=this.length-1;a>=0;a--)this._instances[a].stop()},d._getSlot=function(b,c){var d,e;if(b!=a.INTERRUPT_NONE&&(e=this._get(0),null==e))return!0;for(var f=0,g=this.max;g>f;f++){if(d=this._get(f),null==d)return!0;if(d.playState==a.PLAY_FINISHED||d.playState==a.PLAY_INTERRUPTED||d.playState==a.PLAY_FAILED){e=d;break}b!=a.INTERRUPT_NONE&&(b==a.INTERRUPT_EARLY&&d.getPosition()e.getPosition())&&(e=d)}return null!=e?(e._interrupt(),this._remove(e),!0):!1},d.toString=function(){return"[Sound SoundChannel]"}}(),this.createjs=this.createjs||{},function(){"use strict";var a=function(a,b,c,d){this.EventDispatcher_constructor(),this.src=a,this.uniqueId=-1,this.playState=null,this.delayTimeoutId=null,this._volume=1,Object.defineProperty(this,"volume",{get:this.getVolume,set:this.setVolume}),this._pan=0,Object.defineProperty(this,"pan",{get:this.getPan,set:this.setPan}),this._startTime=Math.max(0,b||0),Object.defineProperty(this,"startTime",{get:this.getStartTime,set:this.setStartTime}),this._duration=Math.max(0,c||0),Object.defineProperty(this,"duration",{get:this.getDuration,set:this.setDuration}),this._playbackResource=null,Object.defineProperty(this,"playbackResource",{get:this.getPlaybackResource,set:this.setPlaybackResource}),d!==!1&&d!==!0&&this.setPlaybackResource(d),this._position=0,Object.defineProperty(this,"position",{get:this.getPosition,set:this.setPosition}),this._loop=0,Object.defineProperty(this,"loop",{get:this.getLoop,set:this.setLoop}),this._muted=!1,Object.defineProperty(this,"muted",{get:this.getMuted,set:this.setMuted}),this._paused=!1,Object.defineProperty(this,"paused",{get:this.getPaused,set:this.setPaused})},b=createjs.extend(a,createjs.EventDispatcher);b.play=function(a,b,c,d,e,f){var g;return g=a instanceof Object||a instanceof createjs.PlayPropsConfig?createjs.PlayPropsConfig.create(a):createjs.PlayPropsConfig.create({interrupt:a,delay:b,offset:c,loop:d,volume:e,pan:f}),this.playState==createjs.Sound.PLAY_SUCCEEDED?(this.applyPlayProps(g),void(this._paused&&this.setPaused(!1))):(this._cleanUp(),createjs.Sound._playInstance(this,g),this)},b.stop=function(){return this._position=0,this._paused=!1,this._handleStop(),this._cleanUp(),this.playState=createjs.Sound.PLAY_FINISHED,this},b.destroy=function(){this._cleanUp(),this.src=null,this.playbackResource=null,this.removeAllEventListeners()},b.applyPlayProps=function(a){return null!=a.offset&&this.setPosition(a.offset),null!=a.loop&&this.setLoop(a.loop),null!=a.volume&&this.setVolume(a.volume),null!=a.pan&&this.setPan(a.pan),null!=a.startTime&&(this.setStartTime(a.startTime),this.setDuration(a.duration)),this},b.toString=function(){return"[AbstractSoundInstance]"},b.getPaused=function(){return this._paused},b.setPaused=function(a){return a!==!0&&a!==!1||this._paused==a||1==a&&this.playState!=createjs.Sound.PLAY_SUCCEEDED?void 0:(this._paused=a,a?this._pause():this._resume(),clearTimeout(this.delayTimeoutId),this)},b.setVolume=function(a){return a==this._volume?this:(this._volume=Math.max(0,Math.min(1,a)),this._muted||this._updateVolume(),this)},b.getVolume=function(){return this._volume},b.setMuted=function(a){return a===!0||a===!1?(this._muted=a,this._updateVolume(),this):void 0},b.getMuted=function(){return this._muted},b.setPan=function(a){return a==this._pan?this:(this._pan=Math.max(-1,Math.min(1,a)),this._updatePan(),this)},b.getPan=function(){return this._pan},b.getPosition=function(){return this._paused||this.playState!=createjs.Sound.PLAY_SUCCEEDED||(this._position=this._calculateCurrentPosition()),this._position},b.setPosition=function(a){return this._position=Math.max(0,a),this.playState==createjs.Sound.PLAY_SUCCEEDED&&this._updatePosition(),this},b.getStartTime=function(){return this._startTime},b.setStartTime=function(a){return a==this._startTime?this:(this._startTime=Math.max(0,a||0),this._updateStartTime(),this)},b.getDuration=function(){return this._duration},b.setDuration=function(a){return a==this._duration?this:(this._duration=Math.max(0,a||0),this._updateDuration(),this)},b.setPlaybackResource=function(a){return this._playbackResource=a,0==this._duration&&this._setDurationFromSource(),this},b.getPlaybackResource=function(){return this._playbackResource},b.getLoop=function(){return this._loop},b.setLoop=function(a){null!=this._playbackResource&&(0!=this._loop&&0==a?this._removeLooping(a):0==this._loop&&0!=a&&this._addLooping(a)),this._loop=a},b._sendEvent=function(a){var b=new createjs.Event(a);this.dispatchEvent(b)},b._cleanUp=function(){clearTimeout(this.delayTimeoutId),this._handleCleanUp(),this._paused=!1,createjs.Sound._playFinished(this)},b._interrupt=function(){this._cleanUp(),this.playState=createjs.Sound.PLAY_INTERRUPTED,this._sendEvent("interrupted")},b._beginPlaying=function(a){return this.setPosition(a.offset),this.setLoop(a.loop),this.setVolume(a.volume),this.setPan(a.pan),null!=a.startTime&&(this.setStartTime(a.startTime),this.setDuration(a.duration)),null!=this._playbackResource&&this._positionc;c++){var e=this._soundInstances[b][c];e.setPlaybackResource(this._audioSources[b]); +}},b._handlePreloadError=function(a){},b._updateVolume=function(){},createjs.AbstractPlugin=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractLoader_constructor(a,!0,createjs.AbstractLoader.SOUND)}var b=createjs.extend(a,createjs.AbstractLoader);a.context=null,b.toString=function(){return"[WebAudioLoader]"},b._createRequest=function(){this._request=new createjs.XHRRequest(this._item,!1),this._request.setResponseType("arraybuffer")},b._sendComplete=function(b){a.context.decodeAudioData(this._rawResult,createjs.proxy(this._handleAudioDecoded,this),createjs.proxy(this._sendError,this))},b._handleAudioDecoded=function(a){this._result=a,this.AbstractLoader__sendComplete()},createjs.WebAudioLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,d,e){this.AbstractSoundInstance_constructor(a,b,d,e),this.gainNode=c.context.createGain(),this.panNode=c.context.createPanner(),this.panNode.panningModel=c._panningModel,this.panNode.connect(this.gainNode),this._updatePan(),this.sourceNode=null,this._soundCompleteTimeout=null,this._sourceNodeNext=null,this._playbackStartTime=0,this._endedHandler=createjs.proxy(this._handleSoundComplete,this)}var b=createjs.extend(a,createjs.AbstractSoundInstance),c=a;c.context=null,c._scratchBuffer=null,c.destinationNode=null,c._panningModel="equalpower",b.destroy=function(){this.AbstractSoundInstance_destroy(),this.panNode.disconnect(0),this.panNode=null,this.gainNode.disconnect(0),this.gainNode=null},b.toString=function(){return"[WebAudioSoundInstance]"},b._updatePan=function(){this.panNode.setPosition(this._pan,0,-.5)},b._removeLooping=function(a){this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext)},b._addLooping=function(a){this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0))},b._setDurationFromSource=function(){this._duration=1e3*this.playbackResource.duration},b._handleCleanUp=function(){this.sourceNode&&this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this.sourceNode=this._cleanUpAudioNode(this.sourceNode),this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext)),0!=this.gainNode.numberOfOutputs&&this.gainNode.disconnect(0),clearTimeout(this._soundCompleteTimeout),this._playbackStartTime=0},b._cleanUpAudioNode=function(a){if(a){a.stop(0),a.disconnect(0);try{a.buffer=c._scratchBuffer}catch(b){}a=null}return a},b._handleSoundReady=function(a){this.gainNode.connect(c.destinationNode);var b=.001*this._duration,d=.001*this._position;d>b&&(d=b),this.sourceNode=this._createAndPlayAudioNode(c.context.currentTime-b,d),this._playbackStartTime=this.sourceNode.startTime-d,this._soundCompleteTimeout=setTimeout(this._endedHandler,1e3*(b-d)),0!=this._loop&&(this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0))},b._createAndPlayAudioNode=function(a,b){var d=c.context.createBufferSource();d.buffer=this.playbackResource,d.connect(this.panNode);var e=.001*this._duration;return d.startTime=a+e,d.start(d.startTime,b+.001*this._startTime,e-b),d},b._pause=function(){this._position=1e3*(c.context.currentTime-this._playbackStartTime),this.sourceNode=this._cleanUpAudioNode(this.sourceNode),this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext),0!=this.gainNode.numberOfOutputs&&this.gainNode.disconnect(0),clearTimeout(this._soundCompleteTimeout)},b._resume=function(){this._handleSoundReady()},b._updateVolume=function(){var a=this._muted?0:this._volume;a!=this.gainNode.gain.value&&(this.gainNode.gain.value=a)},b._calculateCurrentPosition=function(){return 1e3*(c.context.currentTime-this._playbackStartTime)},b._updatePosition=function(){this.sourceNode=this._cleanUpAudioNode(this.sourceNode),this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext),clearTimeout(this._soundCompleteTimeout),this._paused||this._handleSoundReady()},b._handleLoop=function(){this._cleanUpAudioNode(this.sourceNode),this.sourceNode=this._sourceNodeNext,this._playbackStartTime=this.sourceNode.startTime,this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0),this._soundCompleteTimeout=setTimeout(this._endedHandler,this._duration)},b._updateDuration=function(){this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this._pause(),this._resume())},createjs.WebAudioSoundInstance=createjs.promote(a,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function a(){this.AbstractPlugin_constructor(),this._panningModel=c._panningModel,this.context=c.context,this.dynamicsCompressorNode=this.context.createDynamicsCompressor(),this.dynamicsCompressorNode.connect(this.context.destination),this.gainNode=this.context.createGain(),this.gainNode.connect(this.dynamicsCompressorNode),createjs.WebAudioSoundInstance.destinationNode=this.gainNode,this._capabilities=c._capabilities,this._loaderClass=createjs.WebAudioLoader,this._soundInstanceClass=createjs.WebAudioSoundInstance,this._addPropsToClasses()}var b=createjs.extend(a,createjs.AbstractPlugin),c=a;c._capabilities=null,c._panningModel="equalpower",c.context=null,c._scratchBuffer=null,c._unlocked=!1,c.isSupported=function(){var a=createjs.BrowserDetect.isIOS||createjs.BrowserDetect.isAndroid||createjs.BrowserDetect.isBlackberry;return"file:"!=location.protocol||a||this._isFileXHRSupported()?(c._generateCapabilities(),null==c.context?!1:!0):!1},c.playEmptySound=function(){if(null!=c.context){var a=c.context.createBufferSource();a.buffer=c._scratchBuffer,a.connect(c.context.destination),a.start(0,0,0)}},c._isFileXHRSupported=function(){var a=!0,b=new XMLHttpRequest;try{b.open("GET","WebAudioPluginTest.fail",!1)}catch(c){return a=!1}b.onerror=function(){a=!1},b.onload=function(){a=404==this.status||200==this.status||0==this.status&&""!=this.response};try{b.send()}catch(c){a=!1}return a},c._generateCapabilities=function(){if(null==c._capabilities){var a=document.createElement("audio");if(null==a.canPlayType)return null;if(null==c.context)if(window.AudioContext)c.context=new AudioContext;else{if(!window.webkitAudioContext)return null;c.context=new webkitAudioContext}null==c._scratchBuffer&&(c._scratchBuffer=c.context.createBuffer(1,1,22050)),c._compatibilitySetUp(),"ontouchstart"in window&&"running"!=c.context.state&&(c._unlock(),document.addEventListener("mousedown",c._unlock,!0),document.addEventListener("touchend",c._unlock,!0)),c._capabilities={panning:!0,volume:!0,tracks:-1};for(var b=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=b.length;f>e;e++){var g=b[e],h=d[g]||g;c._capabilities[g]="no"!=a.canPlayType("audio/"+g)&&""!=a.canPlayType("audio/"+g)||"no"!=a.canPlayType("audio/"+h)&&""!=a.canPlayType("audio/"+h)}c.context.destination.numberOfChannels<2&&(c._capabilities.panning=!1)}},c._compatibilitySetUp=function(){if(c._panningModel="equalpower",!c.context.createGain){c.context.createGain=c.context.createGainNode;var a=c.context.createBufferSource();a.__proto__.start=a.__proto__.noteGrainOn,a.__proto__.stop=a.__proto__.noteOff,c._panningModel=0}},c._unlock=function(){c._unlocked||(c.playEmptySound(),"running"==c.context.state&&(document.removeEventListener("mousedown",c._unlock,!0),document.removeEventListener("touchend",c._unlock,!0),c._unlocked=!0))},b.toString=function(){return"[WebAudioPlugin]"},b._addPropsToClasses=function(){var a=this._soundInstanceClass;a.context=this.context,a._scratchBuffer=c._scratchBuffer,a.destinationNode=this.gainNode,a._panningModel=this._panningModel,this._loaderClass.context=this.context},b._updateVolume=function(){var a=createjs.Sound._masterMute?0:this._volume;a!=this.gainNode.gain.value&&(this.gainNode.gain.value=a)},createjs.WebAudioPlugin=createjs.promote(a,"AbstractPlugin")}(),this.createjs=this.createjs||{},function(){"use strict";function a(){throw"HTMLAudioTagPool cannot be instantiated"}function b(a){this._tags=[]}var c=a;c._tags={},c._tagPool=new b,c._tagUsed={},c.get=function(a){var b=c._tags[a];return null==b?(b=c._tags[a]=c._tagPool.get(),b.src=a):c._tagUsed[a]?(b=c._tagPool.get(),b.src=a):c._tagUsed[a]=!0,b},c.set=function(a,b){b==c._tags[a]?c._tagUsed[a]=!1:c._tagPool.set(b)},c.remove=function(a){var b=c._tags[a];return null==b?!1:(c._tagPool.set(b),delete c._tags[a],delete c._tagUsed[a],!0)},c.getDuration=function(a){var b=c._tags[a];return null!=b&&b.duration?1e3*b.duration:0},createjs.HTMLAudioTagPool=a;var d=b.prototype;d.constructor=b,d.get=function(){var a;return a=0==this._tags.length?this._createTag():this._tags.pop(),null==a.parentNode&&document.body.appendChild(a),a},d.set=function(a){var b=createjs.indexOf(this._tags,a);-1==b&&(this._tags.src=null,this._tags.push(a))},d.toString=function(){return"[TagPool]"},d._createTag=function(){var a=document.createElement("audio");return a.autoplay=!1,a.preload="none",a}}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c,d){this.AbstractSoundInstance_constructor(a,b,c,d),this._audioSpriteStopTime=null,this._delayTimeoutId=null,this._endedHandler=createjs.proxy(this._handleSoundComplete,this),this._readyHandler=createjs.proxy(this._handleTagReady,this),this._stalledHandler=createjs.proxy(this._playFailed,this),this._audioSpriteEndHandler=createjs.proxy(this._handleAudioSpriteLoop,this),this._loopHandler=createjs.proxy(this._handleSoundComplete,this),c?this._audioSpriteStopTime=.001*(b+c):this._duration=createjs.HTMLAudioTagPool.getDuration(this.src)}var b=createjs.extend(a,createjs.AbstractSoundInstance);b.setMasterVolume=function(a){this._updateVolume()},b.setMasterMute=function(a){this._updateVolume()},b.toString=function(){return"[HTMLAudioSoundInstance]"},b._removeLooping=function(){null!=this._playbackResource&&(this._playbackResource.loop=!1,this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1))},b._addLooping=function(){null==this._playbackResource||this._audioSpriteStopTime||(this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1),this._playbackResource.loop=!0)},b._handleCleanUp=function(){var a=this._playbackResource;if(null!=a){a.pause(),a.loop=!1,a.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED,this._endedHandler,!1),a.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY,this._readyHandler,!1),a.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED,this._stalledHandler,!1),a.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1),a.removeEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE,this._audioSpriteEndHandler,!1);try{a.currentTime=this._startTime}catch(b){}createjs.HTMLAudioTagPool.set(this.src,a),this._playbackResource=null}},b._beginPlaying=function(a){return this._playbackResource=createjs.HTMLAudioTagPool.get(this.src),this.AbstractSoundInstance__beginPlaying(a)},b._handleSoundReady=function(a){if(4!==this._playbackResource.readyState){var b=this._playbackResource;return b.addEventListener(createjs.HTMLAudioPlugin._AUDIO_READY,this._readyHandler,!1),b.addEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED,this._stalledHandler,!1),b.preload="auto",void b.load()}this._updateVolume(),this._playbackResource.currentTime=.001*(this._startTime+this._position),this._audioSpriteStopTime?this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE,this._audioSpriteEndHandler,!1):(this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED,this._endedHandler,!1),0!=this._loop&&(this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1),this._playbackResource.loop=!0)),this._playbackResource.play()},b._handleTagReady=function(a){this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY,this._readyHandler,!1),this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED,this._stalledHandler,!1),this._handleSoundReady()},b._pause=function(){this._playbackResource.pause()},b._resume=function(){this._playbackResource.play()},b._updateVolume=function(){if(null!=this._playbackResource){var a=this._muted||createjs.Sound._masterMute?0:this._volume*createjs.Sound._masterVolume;a!=this._playbackResource.volume&&(this._playbackResource.volume=a)}},b._calculateCurrentPosition=function(){return 1e3*this._playbackResource.currentTime-this._startTime},b._updatePosition=function(){this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1),this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._handleSetPositionSeek,!1);try{this._playbackResource.currentTime=.001*(this._position+this._startTime)}catch(a){this._handleSetPositionSeek(null)}},b._handleSetPositionSeek=function(a){null!=this._playbackResource&&(this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._handleSetPositionSeek,!1),this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1))},b._handleAudioSpriteLoop=function(a){this._playbackResource.currentTime<=this._audioSpriteStopTime||(this._playbackResource.pause(),0==this._loop?this._handleSoundComplete(null):(this._position=0,this._loop--,this._playbackResource.currentTime=.001*this._startTime,this._paused||this._playbackResource.play(),this._sendEvent("loop")))},b._handleLoop=function(a){0==this._loop&&(this._playbackResource.loop=!1,this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1))},b._updateStartTime=function(){this._audioSpriteStopTime=.001*(this._startTime+this._duration),this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED,this._endedHandler,!1),this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE,this._audioSpriteEndHandler,!1))},b._updateDuration=function(){this._audioSpriteStopTime=.001*(this._startTime+this._duration),this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED,this._endedHandler,!1),this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE,this._audioSpriteEndHandler,!1))},b._setDurationFromSource=function(){this._duration=createjs.HTMLAudioTagPool.getDuration(this.src),this._playbackResource=null},createjs.HTMLAudioSoundInstance=createjs.promote(a,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function a(){this.AbstractPlugin_constructor(),this.defaultNumChannels=2,this._capabilities=c._capabilities,this._loaderClass=createjs.SoundLoader,this._soundInstanceClass=createjs.HTMLAudioSoundInstance}var b=createjs.extend(a,createjs.AbstractPlugin),c=a;c.MAX_INSTANCES=30,c._AUDIO_READY="canplaythrough",c._AUDIO_ENDED="ended",c._AUDIO_SEEKED="seeked",c._AUDIO_STALLED="stalled",c._TIME_UPDATE="timeupdate",c._capabilities=null,c.isSupported=function(){return c._generateCapabilities(),null!=c._capabilities},c._generateCapabilities=function(){if(null==c._capabilities){var a=document.createElement("audio");if(null==a.canPlayType)return null;c._capabilities={panning:!1,volume:!0,tracks:-1};for(var b=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=b.length;f>e;e++){var g=b[e],h=d[g]||g;c._capabilities[g]="no"!=a.canPlayType("audio/"+g)&&""!=a.canPlayType("audio/"+g)||"no"!=a.canPlayType("audio/"+h)&&""!=a.canPlayType("audio/"+h)}}},b.register=function(a){var b=createjs.HTMLAudioTagPool.get(a.src),c=this.AbstractPlugin_register(a);return c.setTag(b),c},b.removeSound=function(a){this.AbstractPlugin_removeSound(a),createjs.HTMLAudioTagPool.remove(a)},b.create=function(a,b,c){var d=this.AbstractPlugin_create(a,b,c);return d.setPlaybackResource(null),d},b.toString=function(){return"[HTMLAudioPlugin]"},b.setVolume=b.getVolume=b.setMute=null,createjs.HTMLAudioPlugin=createjs.promote(a,"AbstractPlugin")}(),this.createjs=this.createjs||{},function(){"use strict";function a(b,c,d){this.ignoreGlobalPause=!1,this.loop=!1,this.duration=0,this.pluginData=d||{},this.target=b,this.position=null,this.passive=!1,this._paused=!1,this._curQueueProps={},this._initQueueProps={},this._steps=[],this._actions=[],this._prevPosition=0,this._stepPosition=0,this._prevPos=-1,this._target=b,this._useTicks=!1,this._inited=!1,this._registered=!1,c&&(this._useTicks=c.useTicks,this.ignoreGlobalPause=c.ignoreGlobalPause,this.loop=c.loop,c.onChange&&this.addEventListener("change",c.onChange),c.override&&a.removeTweens(b)),c&&c.paused?this._paused=!0:createjs.Tween._register(this,!0),c&&null!=c.position&&this.setPosition(c.position,a.NONE)}var b=createjs.extend(a,createjs.EventDispatcher);a.NONE=0,a.LOOP=1,a.REVERSE=2,a.IGNORE={},a._tweens=[],a._plugins={},a.get=function(b,c,d,e){return e&&a.removeTweens(b),new a(b,c,d)},a.tick=function(b,c){for(var d=a._tweens.slice(),e=d.length-1;e>=0;e--){var f=d[e];c&&!f.ignoreGlobalPause||f._paused||f.tick(f._useTicks?1:b)}},a.handleEvent=function(a){"tick"==a.type&&this.tick(a.delta,a.paused)},a.removeTweens=function(b){if(b.tweenjs_count){for(var c=a._tweens,d=c.length-1;d>=0;d--){var e=c[d];e._target==b&&(e._paused=!0,c.splice(d,1))}b.tweenjs_count=0}},a.removeAllTweens=function(){for(var b=a._tweens,c=0,d=b.length;d>c;c++){var e=b[c];e._paused=!0,e.target&&(e.target.tweenjs_count=0)}b.length=0},a.hasActiveTweens=function(b){return b?null!=b.tweenjs_count&&!!b.tweenjs_count:a._tweens&&!!a._tweens.length},a.installPlugin=function(b,c){var d=b.priority;null==d&&(b.priority=d=0);for(var e=0,f=c.length,g=a._plugins;f>e;e++){var h=c[e];if(g[h]){for(var i=g[h],j=0,k=i.length;k>j&&!(d=a)return this;var c=this._cloneProps(this._curQueueProps);return this._addStep({d:a,p0:c,e:this._linearEase,p1:c,v:b})},b.to=function(a,b,c){return(isNaN(b)||0>b)&&(b=0),this._addStep({d:b||0,p0:this._cloneProps(this._curQueueProps),e:c,p1:this._cloneProps(this._appendQueueProps(a))})},b.call=function(a,b,c){return this._addAction({f:a,p:b?b:[this],o:c?c:this._target})},b.set=function(a,b){return this._addAction({f:this._set,o:this,p:[a,b?b:this._target]})},b.play=function(a){return a||(a=this),this.call(a.setPaused,[!1],a)},b.pause=function(a){return a||(a=this),this.call(a.setPaused,[!0],a)},b.setPosition=function(a,b){0>a&&(a=0),null==b&&(b=1);var c=a,d=!1;if(c>=this.duration&&(this.loop?c%=this.duration:(c=this.duration,d=!0)),c==this._prevPos)return d;var e=this._prevPos;if(this.position=this._prevPos=c,this._prevPosition=a,this._target)if(d)this._updateTargetProps(null,1);else if(this._steps.length>0){for(var f=0,g=this._steps.length;g>f&&!(this._steps[f].t>c);f++);var h=this._steps[f-1];this._updateTargetProps(h,(this._stepPosition=c-h.t)/h.d)}return 0!=b&&this._actions.length>0&&(this._useTicks?this._runActions(c,c):1==b&&e>c?(e!=this.duration&&this._runActions(e,this.duration),this._runActions(0,c,!0)):this._runActions(e,c)),d&&this.setPaused(!0),this.dispatchEvent("change"),d},b.tick=function(a){this._paused||this.setPosition(this._prevPosition+a)},b.setPaused=function(b){return this._paused===!!b?this:(this._paused=!!b,a._register(this,!b),this)},b.w=b.wait,b.t=b.to,b.c=b.call,b.s=b.set,b.toString=function(){return"[Tween]"},b.clone=function(){throw"Tween can not be cloned."},b._updateTargetProps=function(b,c){var d,e,f,g,h,i;if(b||1!=c){if(this.passive=!!b.v,this.passive)return;b.e&&(c=b.e(c,0,1,1)),d=b.p0,e=b.p1}else this.passive=!1,d=e=this._curQueueProps;for(var j in this._initQueueProps){null==(g=d[j])&&(d[j]=g=this._initQueueProps[j]),null==(h=e[j])&&(e[j]=h=g),f=g==h||0==c||1==c||"number"!=typeof g?1==c?h:g:g+(h-g)*c;var k=!1;if(i=a._plugins[j])for(var l=0,m=i.length;m>l;l++){var n=i[l].tween(this,j,f,d,e,c,!!b&&d==e,!b);n==a.IGNORE?k=!0:f=n}k||(this._target[j]=f)}},b._runActions=function(a,b,c){var d=a,e=b,f=-1,g=this._actions.length,h=1;for(a>b&&(d=b,e=a,f=g,g=h=-1);(f+=h)!=g;){var i=this._actions[f],j=i.t;(j==e||j>d&&e>j||c&&j==a)&&i.f.apply(i.o,i.p)}},b._appendQueueProps=function(b){var c,d,e,f,g;for(var h in b)if(void 0===this._initQueueProps[h]){if(d=this._target[h],c=a._plugins[h])for(e=0,f=c.length;f>e;e++)d=c[e].init(this,h,d);this._initQueueProps[h]=this._curQueueProps[h]=void 0===d?null:d}else d=this._curQueueProps[h];for(var h in b){if(d=this._curQueueProps[h],c=a._plugins[h])for(g=g||{},e=0,f=c.length;f>e;e++)c[e].step&&c[e].step(this,h,d,b[h],g);this._curQueueProps[h]=b[h]}return g&&this._appendQueueProps(g),this._curQueueProps},b._cloneProps=function(a){var b={};for(var c in a)b[c]=a[c];return b},b._addStep=function(a){return a.d>0&&(this._steps.push(a),a.t=this.duration,this.duration+=a.d),this},b._addAction=function(a){return a.t=this.duration,this._actions.push(a),this},b._set=function(a,b){for(var c in a)b[c]=a[c]},createjs.Tween=createjs.promote(a,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.EventDispatcher_constructor(),this.ignoreGlobalPause=!1,this.duration=0,this.loop=!1,this.position=null,this._paused=!1,this._tweens=[],this._labels=null,this._labelList=null,this._prevPosition=0,this._prevPos=-1,this._useTicks=!1,this._registered=!1,c&&(this._useTicks=c.useTicks,this.loop=c.loop,this.ignoreGlobalPause=c.ignoreGlobalPause,c.onChange&&this.addEventListener("change",c.onChange)),a&&this.addTween.apply(this,a),this.setLabels(b),c&&c.paused?this._paused=!0:createjs.Tween._register(this,!0),c&&null!=c.position&&this.setPosition(c.position,createjs.Tween.NONE)}var b=createjs.extend(a,createjs.EventDispatcher);b.addTween=function(a){var b=arguments.length;if(b>1){for(var c=0;b>c;c++)this.addTween(arguments[c]);return arguments[0]}return 0==b?null:(this.removeTween(a),this._tweens.push(a),a.setPaused(!0),a._paused=!1,a._useTicks=this._useTicks,a.duration>this.duration&&(this.duration=a.duration),this._prevPos>=0&&a.setPosition(this._prevPos,createjs.Tween.NONE),a)},b.removeTween=function(a){var b=arguments.length;if(b>1){for(var c=!0,d=0;b>d;d++)c=c&&this.removeTween(arguments[d]);return c}if(0==b)return!1;for(var e=this._tweens,d=e.length;d--;)if(e[d]==a)return e.splice(d,1),a.duration>=this.duration&&this.updateDuration(),!0;return!1},b.addLabel=function(a,b){this._labels[a]=b;var c=this._labelList;if(c){for(var d=0,e=c.length;e>d&&!(bd&&!(b=this.duration;if(c==this._prevPos)return d;this._prevPosition=a,this.position=this._prevPos=c;for(var e=0,f=this._tweens.length;f>e;e++)if(this._tweens[e].setPosition(c,b),c!=this._prevPos)return!1;return d&&this.setPaused(!0),this.dispatchEvent("change"),d},b.setPaused=function(a){this._paused=!!a,createjs.Tween._register(this,!a)},b.updateDuration=function(){this.duration=0;for(var a=0,b=this._tweens.length;b>a;a++){var c=this._tweens[a];c.duration>this.duration&&(this.duration=c.duration)}},b.tick=function(a){this.setPosition(this._prevPosition+a)},b.resolve=function(a){var b=Number(a);return isNaN(b)&&(b=this._labels[a]),b},b.toString=function(){return"[Timeline]"},b.clone=function(){throw"Timeline can not be cloned."},b._goto=function(a){var b=this.resolve(a);null!=b&&this.setPosition(b)},b._calcPosition=function(a){return 0>a?0:aa&&(a=-1),a>1&&(a=1),function(b){return 0==a?b:0>a?b*(b*-a+1+a):b*((2-b)*a+(1-a))}},a.getPowIn=function(a){return function(b){return Math.pow(b,a)}},a.getPowOut=function(a){return function(b){return 1-Math.pow(1-b,a)}},a.getPowInOut=function(a){return function(b){return(b*=2)<1?.5*Math.pow(b,a):1-.5*Math.abs(Math.pow(2-b,a))}},a.quadIn=a.getPowIn(2),a.quadOut=a.getPowOut(2),a.quadInOut=a.getPowInOut(2),a.cubicIn=a.getPowIn(3),a.cubicOut=a.getPowOut(3),a.cubicInOut=a.getPowInOut(3),a.quartIn=a.getPowIn(4),a.quartOut=a.getPowOut(4),a.quartInOut=a.getPowInOut(4),a.quintIn=a.getPowIn(5),a.quintOut=a.getPowOut(5),a.quintInOut=a.getPowInOut(5),a.sineIn=function(a){return 1-Math.cos(a*Math.PI/2)},a.sineOut=function(a){return Math.sin(a*Math.PI/2)},a.sineInOut=function(a){return-.5*(Math.cos(Math.PI*a)-1)},a.getBackIn=function(a){return function(b){return b*b*((a+1)*b-a)}},a.backIn=a.getBackIn(1.7),a.getBackOut=function(a){return function(b){return--b*b*((a+1)*b+a)+1}},a.backOut=a.getBackOut(1.7),a.getBackInOut=function(a){return a*=1.525,function(b){return(b*=2)<1?.5*(b*b*((a+1)*b-a)):.5*((b-=2)*b*((a+1)*b+a)+2)}},a.backInOut=a.getBackInOut(1.7),a.circIn=function(a){return-(Math.sqrt(1-a*a)-1)},a.circOut=function(a){return Math.sqrt(1- --a*a)},a.circInOut=function(a){return(a*=2)<1?-.5*(Math.sqrt(1-a*a)-1):.5*(Math.sqrt(1-(a-=2)*a)+1)},a.bounceIn=function(b){return 1-a.bounceOut(1-b)},a.bounceOut=function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?7.5625*(a-=1.5/2.75)*a+.75:2.5/2.75>a?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375},a.bounceInOut=function(b){return.5>b?.5*a.bounceIn(2*b):.5*a.bounceOut(2*b-1)+.5},a.getElasticIn=function(a,b){var c=2*Math.PI;return function(d){if(0==d||1==d)return d;var e=b/c*Math.asin(1/a);return-(a*Math.pow(2,10*(d-=1))*Math.sin((d-e)*c/b))}},a.elasticIn=a.getElasticIn(1,.3),a.getElasticOut=function(a,b){var c=2*Math.PI;return function(d){if(0==d||1==d)return d;var e=b/c*Math.asin(1/a);return a*Math.pow(2,-10*d)*Math.sin((d-e)*c/b)+1}},a.elasticOut=a.getElasticOut(1,.3),a.getElasticInOut=function(a,b){var c=2*Math.PI;return function(d){var e=b/c*Math.asin(1/a);return(d*=2)<1?-.5*(a*Math.pow(2,10*(d-=1))*Math.sin((d-e)*c/b)):a*Math.pow(2,-10*(d-=1))*Math.sin((d-e)*c/b)*.5+1}},a.elasticInOut=a.getElasticInOut(1,.3*1.5),createjs.Ease=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(){throw"MotionGuidePlugin cannot be instantiated."}a.priority=0,a._rotOffS,a._rotOffE,a._rotNormS,a._rotNormE,a.install=function(){return createjs.Tween.installPlugin(a,["guide","x","y","rotation"]),createjs.Tween.IGNORE},a.init=function(a,b,c){var d=a.target;return d.hasOwnProperty("x")||(d.x=0),d.hasOwnProperty("y")||(d.y=0),d.hasOwnProperty("rotation")||(d.rotation=0),"rotation"==b&&(a.__needsRot=!0),"guide"==b?null:c},a.step=function(b,c,d,e,f){if("rotation"==c&&(b.__rotGlobalS=d,b.__rotGlobalE=e,a.testRotData(b,f)),"guide"!=c)return e;var g,h=e;h.hasOwnProperty("path")||(h.path=[]);var i=h.path;if(h.hasOwnProperty("end")||(h.end=1),h.hasOwnProperty("start")||(h.start=d&&d.hasOwnProperty("end")&&d.path===i?d.end:0),h.hasOwnProperty("_segments")&&h._length)return e;var j=i.length,k=10;if(!(j>=6&&(j-2)%4==0))throw"invalid 'path' data, please see documentation for valid paths";h._segments=[],h._length=0;for(var l=2;j>l;l+=4){for(var m,n,o=i[l-2],p=i[l-1],q=i[l+0],r=i[l+1],s=i[l+2],t=i[l+3],u=o,v=p,w=0,x=[],y=1;k>=y;y++){var z=y/k,A=1-z;m=A*A*o+2*A*z*q+z*z*s,n=A*A*p+2*A*z*r+z*z*t,w+=x[x.push(Math.sqrt((g=m-u)*g+(g=n-v)*g))-1],u=m,v=n}h._segments.push(w),h._segments.push(x),h._length+=w}g=h.orient,h.orient=!0;var B={};return a.calc(h,h.start,B),b.__rotPathS=Number(B.rotation.toFixed(5)),a.calc(h,h.end,B),b.__rotPathE=Number(B.rotation.toFixed(5)),h.orient=!1,a.calc(h,h.end,f),h.orient=g,h.orient?(b.__guideData=h,a.testRotData(b,f),e):e},a.testRotData=function(a,b){if(void 0===a.__rotGlobalS||void 0===a.__rotGlobalE){if(a.__needsRot)return;void 0!==a._curQueueProps.rotation?a.__rotGlobalS=a.__rotGlobalE=a._curQueueProps.rotation:a.__rotGlobalS=a.__rotGlobalE=b.rotation=a.target.rotation||0}if(void 0!==a.__guideData){var c=a.__guideData,d=a.__rotGlobalE-a.__rotGlobalS,e=a.__rotPathE-a.__rotPathS,f=d-e;if("auto"==c.orient)f>180?f-=360:-180>f&&(f+=360);else if("cw"==c.orient){for(;0>f;)f+=360;0==f&&d>0&&180!=d&&(f+=360)}else if("ccw"==c.orient){for(f=d-(e>180?360-e:e);f>0;)f-=360;0==f&&0>d&&-180!=d&&(f-=360)}c.rotDelta=f,c.rotOffS=a.__rotGlobalS-a.__rotPathS,a.__rotGlobalS=a.__rotGlobalE=a.__guideData=a.__needsRot=void 0}},a.tween=function(b,c,d,e,f,g,h,i){var j=f.guide;if(void 0==j||j===e.guide)return d;if(j.lastRatio!=g){var k=(j.end-j.start)*(h?j.end:g)+j.start;switch(a.calc(j,k,b.target),j.orient){case"cw":case"ccw":case"auto":b.target.rotation+=j.rotOffS+j.rotDelta*g;break;case"fixed":default:b.target.rotation+=j.rotOffS}j.lastRatio=g}return"rotation"!=c||j.orient&&"false"!=j.orient?b.target[c]:d},a.calc=function(a,b,c){if(void 0==a._segments)throw"Missing critical pre-calculated information, please file a bug";void 0==c&&(c={x:0,y:0,rotation:0});for(var d=a._segments,e=a.path,f=a._length*b,g=d.length-2,h=0;f>d[h]&&g>h;)f-=d[h],h+=2;var i=d[h+1],j=0;for(g=i.length-1;f>i[j]&&g>j;)f-=i[j],j++;var k=j/++g+f/(g*i[j]);h=2*h+2;var l=1-k;return c.x=l*l*e[h-2]+2*l*k*e[h+0]+k*k*e[h+2],c.y=l*l*e[h-1]+2*l*k*e[h+1]+k*k*e[h+3],a.orient&&(c.rotation=57.2957795*Math.atan2((e[h+1]-e[h-1])*l+(e[h+3]-e[h+1])*k,(e[h+0]-e[h-2])*l+(e[h+2]-e[h+0])*k)),c},createjs.MotionGuidePlugin=a}(),this.createjs=this.createjs||{},function(){"use strict";var a=createjs.TweenJS=createjs.TweenJS||{};a.version="0.6.2",a.buildDate="Thu, 26 Nov 2015 20:44:31 GMT"}(); \ No newline at end of file diff --git a/daydream/public/libs/inflate.min.js b/daydream/public/libs/inflate.min.js new file mode 100644 index 000000000..312b0774f --- /dev/null +++ b/daydream/public/libs/inflate.min.js @@ -0,0 +1,15 @@ +/** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */(function() {'use strict';var l=void 0,aa=this;function r(c,d){var a=c.split("."),b=aa;!(a[0]in b)&&b.execScript&&b.execScript("var "+a[0]);for(var e;a.length&&(e=a.shift());)!a.length&&d!==l?b[e]=d:b=b[e]?b[e]:b[e]={}};var t="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array&&"undefined"!==typeof DataView;function v(c){var d=c.length,a=0,b=Number.POSITIVE_INFINITY,e,f,g,h,k,m,n,p,s,x;for(p=0;pa&&(a=c[p]),c[p]>=1;x=g<<16|p;for(s=m;s>>=1;switch(c){case 0:var d=this.input,a=this.a,b=this.c,e=this.b,f=d.length,g=l,h=l,k=b.length,m=l;this.d=this.f=0;if(a+1>=f)throw Error("invalid uncompressed block header: LEN");g=d[a++]|d[a++]<<8;if(a+1>=f)throw Error("invalid uncompressed block header: NLEN");h=d[a++]|d[a++]<<8;if(g===~h)throw Error("invalid uncompressed block header: length verify");if(a+g>d.length)throw Error("input buffer is broken");switch(this.i){case A:for(;e+ +g>b.length;){m=k-e;g-=m;if(t)b.set(d.subarray(a,a+m),e),e+=m,a+=m;else for(;m--;)b[e++]=d[a++];this.b=e;b=this.e();e=this.b}break;case y:for(;e+g>b.length;)b=this.e({p:2});break;default:throw Error("invalid inflate mode");}if(t)b.set(d.subarray(a,a+g),e),e+=g,a+=g;else for(;g--;)b[e++]=d[a++];this.a=a;this.b=e;this.c=b;break;case 1:this.j(ba,ca);break;case 2:for(var n=C(this,5)+257,p=C(this,5)+1,s=C(this,4)+4,x=new (t?Uint8Array:Array)(D.length),S=l,T=l,U=l,u=l,M=l,F=l,z=l,q=l,V=l,q=0;q=P?8:255>=P?9:279>=P?7:8;var ba=v(O),Q=new (t?Uint8Array:Array)(30),R,ga;R=0;for(ga=Q.length;R=g)throw Error("input buffer is broken");a|=e[f++]<>>d;c.d=b-d;c.a=f;return h} +function E(c,d){for(var a=c.f,b=c.d,e=c.input,f=c.a,g=e.length,h=d[0],k=d[1],m,n;b=g);)a|=e[f++]<>>16;if(n>b)throw Error("invalid code length: "+n);c.f=a>>n;c.d=b-n;c.a=f;return m&65535} +w.prototype.j=function(c,d){var a=this.c,b=this.b;this.o=c;for(var e=a.length-258,f,g,h,k;256!==(f=E(this,c));)if(256>f)b>=e&&(this.b=b,a=this.e(),b=this.b),a[b++]=f;else{g=f-257;k=I[g];0=e&&(this.b=b,a=this.e(),b=this.b);for(;k--;)a[b]=a[b++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=b}; +w.prototype.w=function(c,d){var a=this.c,b=this.b;this.o=c;for(var e=a.length,f,g,h,k;256!==(f=E(this,c));)if(256>f)b>=e&&(a=this.e(),e=a.length),a[b++]=f;else{g=f-257;k=I[g];0e&&(a=this.e(),e=a.length);for(;k--;)a[b]=a[b++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=b}; +w.prototype.e=function(){var c=new (t?Uint8Array:Array)(this.b-32768),d=this.b-32768,a,b,e=this.c;if(t)c.set(e.subarray(32768,c.length));else{a=0;for(b=c.length;aa;++a)e[a]=e[d+a];this.b=32768;return e}; +w.prototype.z=function(c){var d,a=this.input.length/this.a+1|0,b,e,f,g=this.input,h=this.c;c&&("number"===typeof c.p&&(a=c.p),"number"===typeof c.u&&(a+=c.u));2>a?(b=(g.length-this.a)/this.o[2],f=258*(b/2)|0,e=fd&&(this.c.length=d),c=this.c);return this.buffer=c};function W(c,d){var a,b;this.input=c;this.a=0;if(d||!(d={}))d.index&&(this.a=d.index),d.verify&&(this.A=d.verify);a=c[this.a++];b=c[this.a++];switch(a&15){case ha:this.method=ha;break;default:throw Error("unsupported compression method");}if(0!==((a<<8)+b)%31)throw Error("invalid fcheck flag:"+((a<<8)+b)%31);if(b&32)throw Error("fdict flag is not supported");this.q=new w(c,{index:this.a,bufferSize:d.bufferSize,bufferType:d.bufferType,resize:d.resize})} +W.prototype.k=function(){var c=this.input,d,a;d=this.q.k();this.a=this.q.a;if(this.A){a=(c[this.a++]<<24|c[this.a++]<<16|c[this.a++]<<8|c[this.a++])>>>0;var b=d;if("string"===typeof b){var e=b.split(""),f,g;f=0;for(g=e.length;f>>0;b=e}for(var h=1,k=0,m=b.length,n,p=0;0>>0)throw Error("invalid adler-32 checksum");}return d};var ha=8;r("Zlib.Inflate",W);r("Zlib.Inflate.prototype.decompress",W.prototype.k);var X={ADAPTIVE:B.s,BLOCK:B.t},Y,Z,$,ia;if(Object.keys)Y=Object.keys(X);else for(Z in Y=[],$=0,X)Y[$++]=Z;$=0;for(ia=Y.length;$ e + 1E3 && (r.update(1E3 * a / (c - e), 100), e = c, a = 0, t)) { + var d = performance.memory; + t.update(d.usedJSHeapSize / 1048576, d.jsHeapSizeLimit / 1048576) + } + return c + }, + update: function() { + g = this.end() + }, + domElement: c, + setMode: k + } +}; +Stats.Panel = function(h, k, l) { + var c = Infinity, + g = 0, + e = Math.round, + a = e(window.devicePixelRatio || 1), + r = 80 * a, + f = 48 * a, + t = 3 * a, + u = 2 * a, + d = 3 * a, + m = 15 * a, + n = 74 * a, + p = 30 * a, + q = document.createElement("canvas"); + q.width = r; + q.height = f; + q.style.cssText = "width:80px;height:48px"; + var b = q.getContext("2d"); + b.font = "bold " + 9 * a + "px Helvetica,Arial,sans-serif"; + b.textBaseline = "top"; + b.fillStyle = l; + b.fillRect(0, 0, r, f); + b.fillStyle = k; + b.fillText(h, t, u); + b.fillRect(d, m, n, p); + b.fillStyle = l; + b.globalAlpha = .9; + b.fillRect(d, m, n, p); + return { + dom: q, + update: function(f, + v) { + c = Math.min(c, f); + g = Math.max(g, f); + b.fillStyle = l; + b.globalAlpha = 1; + b.fillRect(0, 0, r, m); + b.fillStyle = k; + b.fillText(e(f) + " " + h + " (" + e(c) + "-" + e(g) + ")", t, u); + b.drawImage(q, d + a, m, n - a, p, d, m, n - a, p); + b.fillRect(d + n - a, m, a, p); + b.fillStyle = l; + b.globalAlpha = .9; + b.fillRect(d + n - a, m, a, e((1 - f / v) * p)) + } + } +}; +"object" === typeof module && (module.exports = Stats); + diff --git a/daydream/public/libs/three.min.js b/daydream/public/libs/three.min.js new file mode 100644 index 000000000..8f3610c59 --- /dev/null +++ b/daydream/public/libs/three.min.js @@ -0,0 +1,965 @@ +// threejs.org/license +(function(l,ia){"object"===typeof exports&&"undefined"!==typeof module?ia(exports):"function"===typeof define&&define.amd?define(["exports"],ia):ia(l.THREE={})})(this,function(l){function ia(){}function A(a,b){this.x=a||0;this.y=b||0}function P(){this.elements=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1];0b&&(b=a[c]);return b}function F(){Object.defineProperty(this,"id",{value:Kf+=2});this.uuid=S.generateUUID();this.name="";this.type="BufferGeometry";this.index=null;this.attributes={};this.morphAttributes= +{};this.groups=[];this.boundingSphere=this.boundingBox=null;this.drawRange={start:0,count:Infinity};this.userData={}}function Kb(a,b,c,d,e,f){M.call(this);this.type="BoxGeometry";this.parameters={width:a,height:b,depth:c,widthSegments:d,heightSegments:e,depthSegments:f};this.fromBufferGeometry(new nb(a,b,c,d,e,f));this.mergeVertices()}function nb(a,b,c,d,e,f){function g(a,b,c,d,e,f,g,l,X,B,Lb){var t=f/X,u=g/B,y=f/2,w=g/2,x=l/2;g=X+1;var R=B+1,J=f=0,D,A,z=new p;for(A=0;Am;m++){if(n=d[m])if(h=n[0],k=n[1]){r&&e.addAttribute("morphTarget"+m,r[h]);f&&e.addAttribute("morphNormal"+m,f[h]);c[m]=k;continue}c[m]=0}g.getUniforms().setValue(a,"morphTargetInfluences",c)}}}function Wf(a,b){var c={};return{update:function(d){var e=b.render.frame,f=d.geometry,g=a.get(d,f);c[g.id]!==e&&(f.isGeometry&&g.updateFromObject(d),a.update(g),c[g.id]=e);return g},dispose:function(){c={}}}} +function Ya(a,b,c,d,e,f,g,h,k,m){a=void 0!==a?a:[];Q.call(this,a,void 0!==b?b:301,c,d,e,f,g,h,k,m);this.flipY=!1}function Mb(a,b,c,d){Q.call(this,null);this.image={data:a,width:b,height:c,depth:d};this.minFilter=this.magFilter=1003;this.flipY=this.generateMipmaps=!1}function Nb(a,b,c){var d=a[0];if(0>=d||0/gm,function(a,c){a=K[c];if(void 0===a)throw Error("Can not resolve #include <"+c+">");return Zd(a)})}function Ze(a){return a.replace(/#pragma unroll_loop[\s]+?for \( int i = (\d+); i < (\d+); i \+\+ \) \{([\s\S]+?)(?=\})\}/g,function(a,c,d,e){a="";for(c=parseInt(c);cb||a.height>b){if("data"in a){console.warn("THREE.WebGLRenderer: image in DataTexture is too big ("+a.width+"x"+a.height+").");return}b/=Math.max(a.width,a.height);var c=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");c.width=Math.floor(a.width*b);c.height=Math.floor(a.height*b);c.getContext("2d").drawImage(a,0,0,a.width,a.height,0,0,c.width,c.height);console.warn("THREE.WebGLRenderer: image is too big ("+ +a.width+"x"+a.height+"). Resized to "+c.width+"x"+c.height);return c}return a}function k(a){return S.isPowerOfTwo(a.width)&&S.isPowerOfTwo(a.height)}function m(a,b){return a.generateMipmaps&&b&&1003!==a.minFilter&&1006!==a.minFilter}function r(b,c,e,f){a.generateMipmap(b);d.get(c).__maxMipLevel=Math.log(Math.max(e,f))*Math.LOG2E}function n(b,c){if(!e.isWebGL2)return b;if(b===a.RED){if(c===a.FLOAT)return a.R32F;if(c===a.HALF_FLOAT)return a.R16F;if(c===a.UNSIGNED_BYTE)return a.R8}if(b===a.RGB){if(c=== +a.FLOAT)return a.RGB32F;if(c===a.HALF_FLOAT)return a.RGB16F;if(c===a.UNSIGNED_BYTE)return a.RGB8}if(b===a.RGBA){if(c===a.FLOAT)return a.RGBA32F;if(c===a.HALF_FLOAT)return a.RGBA16F;if(c===a.UNSIGNED_BYTE)return a.RGBA8}return b}function q(b){return 1003===b||1004===b||1005===b?a.NEAREST:a.LINEAR}function v(b){b=b.target;b.removeEventListener("dispose",v);a:{var c=d.get(b);if(b.image&&c.__image__webglTextureCube)a.deleteTexture(c.__image__webglTextureCube);else{if(void 0===c.__webglInit)break a;a.deleteTexture(c.__webglTexture)}d.remove(b)}b.isVideoTexture&& +delete J[b.id];g.memory.textures--}function l(b){b=b.target;b.removeEventListener("dispose",l);var c=d.get(b),e=d.get(b.texture);if(b){void 0!==e.__webglTexture&&a.deleteTexture(e.__webglTexture);b.depthTexture&&b.depthTexture.dispose();if(b.isWebGLRenderTargetCube)for(e=0;6>e;e++)a.deleteFramebuffer(c.__webglFramebuffer[e]),c.__webglDepthbuffer&&a.deleteRenderbuffer(c.__webglDepthbuffer[e]);else a.deleteFramebuffer(c.__webglFramebuffer),c.__webglDepthbuffer&&a.deleteRenderbuffer(c.__webglDepthbuffer); +d.remove(b.texture);d.remove(b)}g.memory.textures--}function u(b,e){var f=d.get(b);if(b.isVideoTexture){var h=b.id,k=g.render.frame;J[h]!==k&&(J[h]=k,b.update())}if(0w;w++)u[w]=q||t?t?b.image[w].image:b.image[w]:h(b.image[w],e.maxCubemapSize);var y=u[0],D=k(y),R=f.convert(b.format),X=f.convert(b.type),B=n(R,X);p(a.TEXTURE_CUBE_MAP,b,D);for(w=0;6>w;w++)if(q)for(var x,J=u[w].mipmaps,A=0,z=J.length;Aq;q++)e.__webglFramebuffer[q]=a.createFramebuffer()}else e.__webglFramebuffer=a.createFramebuffer();if(h){c.bindTexture(a.TEXTURE_CUBE_MAP,f.__webglTexture);p(a.TEXTURE_CUBE_MAP,b.texture,n);for(q=0;6>q;q++)w(e.__webglFramebuffer[q],b,a.COLOR_ATTACHMENT0,a.TEXTURE_CUBE_MAP_POSITIVE_X+q);m(b.texture,n)&&r(a.TEXTURE_CUBE_MAP,b.texture,b.width,b.height);c.bindTexture(a.TEXTURE_CUBE_MAP,null)}else c.bindTexture(a.TEXTURE_2D, +f.__webglTexture),p(a.TEXTURE_2D,b.texture,n),w(e.__webglFramebuffer,b,a.COLOR_ATTACHMENT0,a.TEXTURE_2D),m(b.texture,n)&&r(a.TEXTURE_2D,b.texture,b.width,b.height),c.bindTexture(a.TEXTURE_2D,null);if(b.depthBuffer){e=d.get(b);f=!0===b.isWebGLRenderTargetCube;if(b.depthTexture){if(f)throw Error("target.depthTexture not supported in Cube render targets");if(b&&b.isWebGLRenderTargetCube)throw Error("Depth Texture with cube render targets is not supported");a.bindFramebuffer(a.FRAMEBUFFER,e.__webglFramebuffer); +if(!b.depthTexture||!b.depthTexture.isDepthTexture)throw Error("renderTarget.depthTexture must be an instance of THREE.DepthTexture");d.get(b.depthTexture).__webglTexture&&b.depthTexture.image.width===b.width&&b.depthTexture.image.height===b.height||(b.depthTexture.image.width=b.width,b.depthTexture.image.height=b.height,b.depthTexture.needsUpdate=!0);u(b.depthTexture,0);e=d.get(b.depthTexture).__webglTexture;if(1026===b.depthTexture.format)a.framebufferTexture2D(a.FRAMEBUFFER,a.DEPTH_ATTACHMENT, +a.TEXTURE_2D,e,0);else if(1027===b.depthTexture.format)a.framebufferTexture2D(a.FRAMEBUFFER,a.DEPTH_STENCIL_ATTACHMENT,a.TEXTURE_2D,e,0);else throw Error("Unknown depthTexture format");}else if(f)for(e.__webglDepthbuffer=[],f=0;6>f;f++)a.bindFramebuffer(a.FRAMEBUFFER,e.__webglFramebuffer[f]),e.__webglDepthbuffer[f]=a.createRenderbuffer(),D(e.__webglDepthbuffer[f],b);else a.bindFramebuffer(a.FRAMEBUFFER,e.__webglFramebuffer),e.__webglDepthbuffer=a.createRenderbuffer(),D(e.__webglDepthbuffer,b);a.bindFramebuffer(a.FRAMEBUFFER, +null)}};this.updateRenderTargetMipmap=function(b){var e=b.texture,f=k(b);if(m(e,f)){f=b.isWebGLRenderTargetCube?a.TEXTURE_CUBE_MAP:a.TEXTURE_2D;var g=d.get(e).__webglTexture;c.bindTexture(f,g);r(f,e,b.width,b.height);c.bindTexture(f,null)}}}function bf(a,b,c){return{convert:function(d){if(1E3===d)return a.REPEAT;if(1001===d)return a.CLAMP_TO_EDGE;if(1002===d)return a.MIRRORED_REPEAT;if(1003===d)return a.NEAREST;if(1004===d)return a.NEAREST_MIPMAP_NEAREST;if(1005===d)return a.NEAREST_MIPMAP_LINEAR; +if(1006===d)return a.LINEAR;if(1007===d)return a.LINEAR_MIPMAP_NEAREST;if(1008===d)return a.LINEAR_MIPMAP_LINEAR;if(1009===d)return a.UNSIGNED_BYTE;if(1017===d)return a.UNSIGNED_SHORT_4_4_4_4;if(1018===d)return a.UNSIGNED_SHORT_5_5_5_1;if(1019===d)return a.UNSIGNED_SHORT_5_6_5;if(1010===d)return a.BYTE;if(1011===d)return a.SHORT;if(1012===d)return a.UNSIGNED_SHORT;if(1013===d)return a.INT;if(1014===d)return a.UNSIGNED_INT;if(1015===d)return a.FLOAT;if(1016===d){if(c.isWebGL2)return a.HALF_FLOAT;var e= +b.get("OES_texture_half_float");if(null!==e)return e.HALF_FLOAT_OES}if(1021===d)return a.ALPHA;if(1022===d)return a.RGB;if(1023===d)return a.RGBA;if(1024===d)return a.LUMINANCE;if(1025===d)return a.LUMINANCE_ALPHA;if(1026===d)return a.DEPTH_COMPONENT;if(1027===d)return a.DEPTH_STENCIL;if(1028===d)return a.RED;if(100===d)return a.FUNC_ADD;if(101===d)return a.FUNC_SUBTRACT;if(102===d)return a.FUNC_REVERSE_SUBTRACT;if(200===d)return a.ZERO;if(201===d)return a.ONE;if(202===d)return a.SRC_COLOR;if(203=== +d)return a.ONE_MINUS_SRC_COLOR;if(204===d)return a.SRC_ALPHA;if(205===d)return a.ONE_MINUS_SRC_ALPHA;if(206===d)return a.DST_ALPHA;if(207===d)return a.ONE_MINUS_DST_ALPHA;if(208===d)return a.DST_COLOR;if(209===d)return a.ONE_MINUS_DST_COLOR;if(210===d)return a.SRC_ALPHA_SATURATE;if(33776===d||33777===d||33778===d||33779===d)if(e=b.get("WEBGL_compressed_texture_s3tc"),null!==e){if(33776===d)return e.COMPRESSED_RGB_S3TC_DXT1_EXT;if(33777===d)return e.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(33778===d)return e.COMPRESSED_RGBA_S3TC_DXT3_EXT; +if(33779===d)return e.COMPRESSED_RGBA_S3TC_DXT5_EXT}if(35840===d||35841===d||35842===d||35843===d)if(e=b.get("WEBGL_compressed_texture_pvrtc"),null!==e){if(35840===d)return e.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(35841===d)return e.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(35842===d)return e.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(35843===d)return e.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}if(36196===d&&(e=b.get("WEBGL_compressed_texture_etc1"),null!==e))return e.COMPRESSED_RGB_ETC1_WEBGL;if(37808===d||37809===d||37810=== +d||37811===d||37812===d||37813===d||37814===d||37815===d||37816===d||37817===d||37818===d||37819===d||37820===d||37821===d)if(e=b.get("WEBGL_compressed_texture_astc"),null!==e)return d;if(103===d||104===d){if(c.isWebGL2){if(103===d)return a.MIN;if(104===d)return a.MAX}e=b.get("EXT_blend_minmax");if(null!==e){if(103===d)return e.MIN_EXT;if(104===d)return e.MAX_EXT}}if(1020===d){if(c.isWebGL2)return a.UNSIGNED_INT_24_8;e=b.get("WEBGL_depth_texture");if(null!==e)return e.UNSIGNED_INT_24_8_WEBGL}return 0}}} +function Ob(){C.call(this);this.type="Group"}function Ra(){C.call(this);this.type="Camera";this.matrixWorldInverse=new P;this.projectionMatrix=new P;this.projectionMatrixInverse=new P}function V(a,b,c,d){Ra.call(this);this.type="PerspectiveCamera";this.fov=void 0!==a?a:50;this.zoom=1;this.near=void 0!==c?c:.1;this.far=void 0!==d?d:2E3;this.focus=10;this.aspect=void 0!==b?b:1;this.view=null;this.filmGauge=35;this.filmOffset=0;this.updateProjectionMatrix()}function Cc(a){V.call(this);this.cameras=a|| +[]}function cf(a){function b(){return null!==e&&!0===e.isPresenting}function c(){if(b()){var c=e.getEyeParameters("left"),f=c.renderWidth;c=c.renderHeight;w=a.getPixelRatio();x=a.getSize();a.setDrawingBufferSize(2*f,c,1);J.start()}else d.enabled&&a.setDrawingBufferSize(x.width,x.height,w),J.stop()}var d=this,e=null,f=null,g=null,h=[],k=new P,m=new P,r="stage";"undefined"!==typeof window&&"VRFrameData"in window&&(f=new window.VRFrameData,window.addEventListener("vrdisplaypresentchange",c,!1));var n= +new P,q=new ja,v=new p,l=new V;l.bounds=new Z(0,0,.5,1);l.layers.enable(1);var u=new V;u.bounds=new Z(.5,0,.5,1);u.layers.enable(2);var y=new Cc([l,u]);y.layers.enable(1);y.layers.enable(2);var x,w,D=[];this.enabled=!1;this.getController=function(a){var b=h[a];void 0===b&&(b=new Ob,b.matrixAutoUpdate=!1,b.visible=!1,h[a]=b);return b};this.getDevice=function(){return e};this.setDevice=function(a){void 0!==a&&(e=a);J.setContext(a)};this.setFrameOfReferenceType=function(a){r=a};this.setPoseTarget=function(a){void 0!== +a&&(g=a)};this.getCamera=function(a){var b="stage"===r?1.6:0;if(null===e)return a.position.set(0,b,0),a;e.depthNear=a.near;e.depthFar=a.far;e.getFrameData(f);if("stage"===r){var c=e.stageParameters;c?k.fromArray(c.sittingToStandingTransform):k.makeTranslation(0,b,0)}b=f.pose;c=null!==g?g:a;c.matrix.copy(k);c.matrix.decompose(c.position,c.quaternion,c.scale);null!==b.orientation&&(q.fromArray(b.orientation),c.quaternion.multiply(q));null!==b.position&&(q.setFromRotationMatrix(k),v.fromArray(b.position), +v.applyQuaternion(q),c.position.add(v));c.updateMatrixWorld();if(!1===e.isPresenting)return a;l.near=a.near;u.near=a.near;l.far=a.far;u.far=a.far;y.matrixWorld.copy(a.matrixWorld);y.matrixWorldInverse.copy(a.matrixWorldInverse);l.matrixWorldInverse.fromArray(f.leftViewMatrix);u.matrixWorldInverse.fromArray(f.rightViewMatrix);m.getInverse(k);"stage"===r&&(l.matrixWorldInverse.multiply(m),u.matrixWorldInverse.multiply(m));a=c.parent;null!==a&&(n.getInverse(a.matrixWorld),l.matrixWorldInverse.multiply(n), +u.matrixWorldInverse.multiply(n));l.matrixWorld.getInverse(l.matrixWorldInverse);u.matrixWorld.getInverse(u.matrixWorldInverse);l.projectionMatrix.fromArray(f.leftProjectionMatrix);u.projectionMatrix.fromArray(f.rightProjectionMatrix);y.projectionMatrix.copy(l.projectionMatrix);a=e.getLayers();a.length&&(a=a[0],null!==a.leftBounds&&4===a.leftBounds.length&&l.bounds.fromArray(a.leftBounds),null!==a.rightBounds&&4===a.rightBounds.length&&u.bounds.fromArray(a.rightBounds));a:for(a=0;af.normalMatrix.determinant();ba.setMaterial(e,h);var k=q(a,c,e,f),m=!1;if(b!==d.id||K!==k.id||sd!==(!0===e.wireframe))b=d.id,K=k.id,sd=!0===e.wireframe,m=!0;f.morphTargetInfluences&&(xa.update(f,d,e,k),m=!0);h=d.index;var r=d.attributes.position;c=1;!0===e.wireframe&&(h=ta.getWireframeAttribute(d),c=2);a=ya;if(null!==h){var n=ra.get(h);a=Aa;a.setIndex(n)}if(m){if(d&&d.isInstancedBufferGeometry&!wa.isWebGL2&&null===ka.get("ANGLE_instanced_arrays"))console.error("THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays."); +else{ba.initAttributes();m=d.attributes;k=k.getAttributes();var l=e.defaultAttributeValues;for(B in k){var v=k[B];if(0<=v){var t=m[B];if(void 0!==t){var u=t.normalized,p=t.itemSize,w=ra.get(t);if(void 0!==w){var y=w.buffer,x=w.type;w=w.bytesPerElement;if(t.isInterleavedBufferAttribute){var J=t.data,D=J.stride;t=t.offset;J&&J.isInstancedInterleavedBuffer?(ba.enableAttributeAndDivisor(v,J.meshPerAttribute),void 0===d.maxInstancedCount&&(d.maxInstancedCount=J.meshPerAttribute*J.count)):ba.enableAttribute(v); +E.bindBuffer(E.ARRAY_BUFFER,y);E.vertexAttribPointer(v,p,x,u,D*w,t*w)}else t.isInstancedBufferAttribute?(ba.enableAttributeAndDivisor(v,t.meshPerAttribute),void 0===d.maxInstancedCount&&(d.maxInstancedCount=t.meshPerAttribute*t.count)):ba.enableAttribute(v),E.bindBuffer(E.ARRAY_BUFFER,y),E.vertexAttribPointer(v,p,x,u,0,0)}}else if(void 0!==l&&(u=l[B],void 0!==u))switch(u.length){case 2:E.vertexAttrib2fv(v,u);break;case 3:E.vertexAttrib3fv(v,u);break;case 4:E.vertexAttrib4fv(v,u);break;default:E.vertexAttrib1fv(v, +u)}}}ba.disableUnusedAttributes()}null!==h&&E.bindBuffer(E.ELEMENT_ARRAY_BUFFER,n.buffer)}n=Infinity;null!==h?n=h.count:void 0!==r&&(n=r.count);h=d.drawRange.start*c;r=null!==g?g.start*c:0;var B=Math.max(h,r);g=Math.max(0,Math.min(n,h+d.drawRange.count*c,r+(null!==g?g.count*c:Infinity))-1-B+1);if(0!==g){if(f.isMesh)if(!0===e.wireframe)ba.setLineWidth(e.wireframeLinewidth*(null===M?U:1)),a.setMode(E.LINES);else switch(f.drawMode){case 0:a.setMode(E.TRIANGLES);break;case 1:a.setMode(E.TRIANGLE_STRIP); +break;case 2:a.setMode(E.TRIANGLE_FAN)}else f.isLine?(e=e.linewidth,void 0===e&&(e=1),ba.setLineWidth(e*(null===M?U:1)),f.isLineSegments?a.setMode(E.LINES):f.isLineLoop?a.setMode(E.LINE_LOOP):a.setMode(E.LINE_STRIP)):f.isPoints?a.setMode(E.POINTS):f.isSprite&&a.setMode(E.TRIANGLES);d&&d.isInstancedBufferGeometry?0=wa.maxTextures&&console.warn("THREE.WebGLRenderer: Trying to use "+a+" texture units while this GPU supports only "+wa.maxTextures);ca+=1;return a};this.setTexture2D=function(){var a=!1;return function(b,c){b&&b.isWebGLRenderTarget&&(a||(console.warn("THREE.WebGLRenderer.setTexture2D: don't use render targets as textures. Use their .texture property instead."),a=!0),b=b.texture);ha.setTexture2D(b,c)}}();this.setTexture3D=function(){return function(a,b){ha.setTexture3D(a,b)}}();this.setTexture= +function(){var a=!1;return function(b,c){a||(console.warn("THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead."),a=!0);ha.setTexture2D(b,c)}}();this.setTextureCube=function(){var a=!1;return function(b,c){b&&b.isWebGLRenderTargetCube&&(a||(console.warn("THREE.WebGLRenderer.setTextureCube: don't use cube render targets as textures. Use their .texture property instead."),a=!0),b=b.texture);b&&b.isCubeTexture||Array.isArray(b.image)&&6===b.image.length?ha.setTextureCube(b,c):ha.setTextureCubeDynamic(b, +c)}}();this.setFramebuffer=function(a){N=a};this.getRenderTarget=function(){return M};this.setRenderTarget=function(a){(M=a)&&void 0===Da.get(a).__webglFramebuffer&&ha.setupRenderTarget(a);var b=N,c=!1;a?(b=Da.get(a).__webglFramebuffer,a.isWebGLRenderTargetCube&&(b=b[a.activeCubeFace],c=!0),W.copy(a.viewport),Bc.copy(a.scissor),ea=a.scissorTest):(W.copy(fa).multiplyScalar(U),Bc.copy(ja).multiplyScalar(U),ea=sa);L!==b&&(E.bindFramebuffer(E.FRAMEBUFFER,b),L=b);ba.viewport(W);ba.scissor(Bc);ba.setScissorTest(ea); +c&&(c=Da.get(a.texture),E.framebufferTexture2D(E.FRAMEBUFFER,E.COLOR_ATTACHMENT0,E.TEXTURE_CUBE_MAP_POSITIVE_X+a.activeCubeFace,c.__webglTexture,a.activeMipMapLevel))};this.readRenderTargetPixels=function(a,b,c,d,e,f){if(a&&a.isWebGLRenderTarget){var g=Da.get(a).__webglFramebuffer;if(g){var h=!1;g!==L&&(E.bindFramebuffer(E.FRAMEBUFFER,g),h=!0);try{var k=a.texture,m=k.format,r=k.type;1023!==m&&ia.convert(m)!==E.getParameter(E.IMPLEMENTATION_COLOR_READ_FORMAT)?console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format."): +1009===r||ia.convert(r)===E.getParameter(E.IMPLEMENTATION_COLOR_READ_TYPE)||1015===r&&(wa.isWebGL2||ka.get("OES_texture_float")||ka.get("WEBGL_color_buffer_float"))||1016===r&&(wa.isWebGL2?ka.get("EXT_color_buffer_float"):ka.get("EXT_color_buffer_half_float"))?E.checkFramebufferStatus(E.FRAMEBUFFER)===E.FRAMEBUFFER_COMPLETE?0<=b&&b<=a.width-d&&0<=c&&c<=a.height-e&&E.readPixels(b,c,d,e,ia.convert(m),ia.convert(r),f):console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete."): +console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.")}finally{h&&E.bindFramebuffer(E.FRAMEBUFFER,L)}}}else console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.")};this.copyFramebufferToTexture=function(a,b,c){var d=b.image.width,e=b.image.height,f=ia.convert(b.format);this.setTexture2D(b,0);E.copyTexImage2D(E.TEXTURE_2D,c||0,f,a.x,a.y,d,e,0)};this.copyTextureToTexture= +function(a,b,c,d){var e=b.image.width,f=b.image.height,g=ia.convert(c.format),h=ia.convert(c.type);this.setTexture2D(c,0);b.isDataTexture?E.texSubImage2D(E.TEXTURE_2D,d||0,a.x,a.y,e,f,g,h,b.image.data):E.texSubImage2D(E.TEXTURE_2D,d||0,a.x,a.y,g,h,b.image)}}function Pb(a,b){this.name="";this.color=new G(a);this.density=void 0!==b?b:2.5E-4}function Qb(a,b,c){this.name="";this.color=new G(a);this.near=void 0!==b?b:1;this.far=void 0!==c?c:1E3}function vd(){C.call(this);this.type="Scene";this.overrideMaterial= +this.fog=this.background=null;this.autoUpdate=!0}function qb(a,b){this.array=a;this.stride=b;this.count=void 0!==a?a.length/b:0;this.dynamic=!1;this.updateRange={offset:0,count:-1};this.version=0}function Dc(a,b,c,d){this.data=a;this.itemSize=b;this.offset=c;this.normalized=!0===d}function fb(a){L.call(this);this.type="SpriteMaterial";this.color=new G(16777215);this.map=null;this.rotation=0;this.sizeAttenuation=!0;this.lights=!1;this.transparent=!0;this.setValues(a)}function Ec(a){C.call(this);this.type= +"Sprite";if(void 0===Rb){Rb=new F;var b=new Float32Array([-.5,-.5,0,0,0,.5,-.5,0,1,0,.5,.5,0,1,1,-.5,.5,0,0,1]);b=new qb(b,5);Rb.setIndex([0,1,2,0,2,3]);Rb.addAttribute("position",new Dc(b,3,0,!1));Rb.addAttribute("uv",new Dc(b,2,3,!1))}this.geometry=Rb;this.material=void 0!==a?a:new fb;this.center=new A(.5,.5)}function Fc(){C.call(this);this.type="LOD";Object.defineProperties(this,{levels:{enumerable:!0,value:[]}})}function Gc(a,b){a=a||[];this.bones=a.slice(0);this.boneMatrices=new Float32Array(16* +this.bones.length);if(void 0===b)this.calculateInverses();else if(this.bones.length===b.length)this.boneInverses=b.slice(0);else for(console.warn("THREE.Skeleton boneInverses is the wrong length."),this.boneInverses=[],a=0,b=this.bones.length;ac;c++){var n=r[h[c]];var q=r[h[(c+1)%3]];f[0]=Math.min(n,q);f[1]=Math.max(n,q);n=f[0]+","+f[1];void 0===g[n]&&(g[n]={index1:f[0],index2:f[1]})}}for(n in g)m=g[n],h=a.vertices[m.index1], +b.push(h.x,h.y,h.z),h=a.vertices[m.index2],b.push(h.x,h.y,h.z)}else if(a&&a.isBufferGeometry)if(h=new p,null!==a.index){k=a.attributes.position;r=a.index;var l=a.groups;0===l.length&&(l=[{start:0,count:r.count,materialIndex:0}]);a=0;for(e=l.length;ac;c++)n=r.getX(m+c),q=r.getX(m+(c+1)%3),f[0]=Math.min(n,q),f[1]=Math.max(n,q),n=f[0]+","+f[1],void 0===g[n]&&(g[n]={index1:f[0],index2:f[1]});for(n in g)m=g[n],h.fromBufferAttribute(k,m.index1), +b.push(h.x,h.y,h.z),h.fromBufferAttribute(k,m.index2),b.push(h.x,h.y,h.z)}else for(k=a.attributes.position,m=0,d=k.count/3;mc;c++)g=3*m+c,h.fromBufferAttribute(k,g),b.push(h.x,h.y,h.z),g=3*m+(c+1)%3,h.fromBufferAttribute(k,g),b.push(h.x,h.y,h.z);this.addAttribute("position",new z(b,3))}function Jc(a,b,c){M.call(this);this.type="ParametricGeometry";this.parameters={func:a,slices:b,stacks:c};this.fromBufferGeometry(new Vb(a,b,c));this.mergeVertices()}function Vb(a,b,c){F.call(this); +this.type="ParametricBufferGeometry";this.parameters={func:a,slices:b,stacks:c};var d=[],e=[],f=[],g=[],h=new p,k=new p,m=new p,r=new p,n=new p,q,l;3>a.length&&console.error("THREE.ParametricGeometry: Function must now modify a Vector3 as third parameter.");var t=b+1;for(q=0;q<=c;q++){var u=q/c;for(l=0;l<=b;l++){var y=l/b;a(y,u,k);e.push(k.x,k.y,k.z);0<=y-1E-5?(a(y-1E-5,u,m),r.subVectors(k,m)):(a(y+1E-5,u,m),r.subVectors(m,k));0<=u-1E-5?(a(y,u-1E-5,m),n.subVectors(k,m)):(a(y,u+1E-5,m),n.subVectors(m, +k));h.crossVectors(r,n).normalize();f.push(h.x,h.y,h.z);g.push(y,u)}}for(q=0;qd&&1===a.x&&(k[b]=a.x-1);0===c.x&&0===c.z&&(k[b]=d/2/Math.PI+.5)}F.call(this);this.type="PolyhedronBufferGeometry";this.parameters={vertices:a,indices:b,radius:c,detail:d};c=c||1;d=d||0;var h=[],k=[];(function(a){for(var c=new p,d=new p,g=new p,h=0;he&&(.2>b&&(k[a+0]+=1),.2>c&&(k[a+2]+=1),.2>d&&(k[a+4]+=1))})();this.addAttribute("position",new z(h,3));this.addAttribute("normal",new z(h.slice(),3));this.addAttribute("uv",new z(k,2));0===d?this.computeVertexNormals():this.normalizeNormals()}function Lc(a,b){M.call(this);this.type="TetrahedronGeometry";this.parameters={radius:a,detail:b};this.fromBufferGeometry(new Wb(a,b));this.mergeVertices()}function Wb(a,b){ma.call(this,[1,1,1,-1,-1,1,-1,1,-1,1,-1,-1],[2,1,0,0,3,2,1,3,0,2,3,1],a,b); +this.type="TetrahedronBufferGeometry";this.parameters={radius:a,detail:b}}function Mc(a,b){M.call(this);this.type="OctahedronGeometry";this.parameters={radius:a,detail:b};this.fromBufferGeometry(new rb(a,b));this.mergeVertices()}function rb(a,b){ma.call(this,[1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1],[0,2,4,0,4,3,0,3,5,0,5,2,1,2,5,1,5,3,1,3,4,1,4,2],a,b);this.type="OctahedronBufferGeometry";this.parameters={radius:a,detail:b}}function Nc(a,b){M.call(this);this.type="IcosahedronGeometry";this.parameters= +{radius:a,detail:b};this.fromBufferGeometry(new Xb(a,b));this.mergeVertices()}function Xb(a,b){var c=(1+Math.sqrt(5))/2;ma.call(this,[-1,c,0,1,c,0,-1,-c,0,1,-c,0,0,-1,c,0,1,c,0,-1,-c,0,1,-c,c,0,-1,c,0,1,-c,0,-1,-c,0,1],[0,11,5,0,5,1,0,1,7,0,7,10,0,10,11,1,5,9,5,11,4,11,10,2,10,7,6,7,1,8,3,9,4,3,4,2,3,2,6,3,6,8,3,8,9,4,9,5,2,4,11,6,2,10,8,6,7,9,8,1],a,b);this.type="IcosahedronBufferGeometry";this.parameters={radius:a,detail:b}}function Oc(a,b){M.call(this);this.type="DodecahedronGeometry";this.parameters= +{radius:a,detail:b};this.fromBufferGeometry(new Yb(a,b));this.mergeVertices()}function Yb(a,b){var c=(1+Math.sqrt(5))/2,d=1/c;ma.call(this,[-1,-1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,1,1,1,0,-d,-c,0,-d,c,0,d,-c,0,d,c,-d,-c,0,-d,c,0,d,-c,0,d,c,0,-c,0,-d,c,0,-d,-c,0,d,c,0,d],[3,11,7,3,7,15,3,15,13,7,19,17,7,17,6,7,6,15,17,4,8,17,8,10,17,10,6,8,0,16,8,16,2,8,2,10,0,12,1,0,1,18,0,18,16,6,10,2,6,2,13,6,13,15,2,16,18,2,18,3,2,3,13,18,1,9,18,9,11,18,11,3,4,14,12,4,12,0,4,0,8,11,9,5,11,5,19,11, +19,7,19,5,14,19,14,4,19,4,17,1,12,14,1,14,5,1,5,9],a,b);this.type="DodecahedronBufferGeometry";this.parameters={radius:a,detail:b}}function Pc(a,b,c,d,e,f){M.call(this);this.type="TubeGeometry";this.parameters={path:a,tubularSegments:b,radius:c,radialSegments:d,closed:e};void 0!==f&&console.warn("THREE.TubeGeometry: taper has been removed.");a=new Zb(a,b,c,d,e);this.tangents=a.tangents;this.normals=a.normals;this.binormals=a.binormals;this.fromBufferGeometry(a);this.mergeVertices()}function Zb(a, +b,c,d,e){function f(e){r=a.getPointAt(e/b,r);var f=g.normals[e];e=g.binormals[e];for(q=0;q<=d;q++){var m=q/d*Math.PI*2,n=Math.sin(m);m=-Math.cos(m);k.x=m*f.x+n*e.x;k.y=m*f.y+n*e.y;k.z=m*f.z+n*e.z;k.normalize();t.push(k.x,k.y,k.z);h.x=r.x+c*k.x;h.y=r.y+c*k.y;h.z=r.z+c*k.z;l.push(h.x,h.y,h.z)}}F.call(this);this.type="TubeBufferGeometry";this.parameters={path:a,tubularSegments:b,radius:c,radialSegments:d,closed:e};b=b||64;c=c||1;d=d||8;e=e||!1;var g=a.computeFrenetFrames(b,e);this.tangents=g.tangents; +this.normals=g.normals;this.binormals=g.binormals;var h=new p,k=new p,m=new A,r=new p,n,q,l=[],t=[],u=[],y=[];for(n=0;n=b;e-=d)f=ef(e,a[e],a[e+1],f);f&&sb(f,f.next)&&(Sc(f),f=f.next);return f}function Tc(a,b){if(!a)return a;b||(b=a);do{var c=!1;if(a.steiner||!sb(a,a.next)&&0!==na(a.prev,a,a.next))a=a.next;else{Sc(a);a=b=a.prev;if(a===a.next)break;c=!0}}while(c||a!==b);return b}function Uc(a,b,c,d,e,f,g){if(a){if(!g&&f){var h=a,k=h;do null===k.z&&(k.z=ce(k.x,k.y,d,e,f)),k.prevZ=k.prev,k=k.nextZ=k.next;while(k!==h);k.prevZ.nextZ=null;k.prevZ=null;h=k;var m,r,n,l,v=1;do{k=h;var t=h=null;for(r=0;k;){r++; +var u=k;for(m=n=0;mn.x?r.x>v.x?r.x:v.x:n.x>v.x?n.x:v.x,D=r.y>n.y?r.y>v.y?r.y:v.y:n.y>v.y?n.y:v.y;m=ce(r.x=m;){if(p!==t.prev&&p!==t.next&&zd(r.x,r.y,n.x,n.y,v.x,v.y,p.x,p.y)&&0<=na(p.prev,p,p.next)){t=!1;break a}p=p.prevZ}t=!0}}else a:if(t=a,r=t.prev,n=t,v=t.next,0<=na(r,n,v))t=!1;else{for(m=t.next.next;m!==t.prev;){if(zd(r.x,r.y,n.x,n.y,v.x,v.y,m.x,m.y)&&0<=na(m.prev,m,m.next)){t=!1;break a}m=m.next}t=!0}if(t)b.push(k.i/c),b.push(a.i/ +c),b.push(u.i/c),Sc(a),h=a=u.next;else if(a=u,a===h){if(!g)Uc(Tc(a),b,c,d,e,f,1);else if(1===g){g=b;h=c;k=a;do u=k.prev,t=k.next.next,!sb(u,t)&&ff(u,k,k.next,t)&&Vc(u,t)&&Vc(t,u)&&(g.push(u.i/h),g.push(k.i/h),g.push(t.i/h),Sc(k),Sc(k.next),k=a=t),k=k.next;while(k!==a);a=k;Uc(a,b,c,d,e,f,2)}else if(2===g)a:{g=a;do{for(h=g.next.next;h!==g.prev;){if(k=g.i!==h.i){k=g;u=h;if(t=k.next.i!==u.i&&k.prev.i!==u.i){b:{t=k;do{if(t.i!==k.i&&t.next.i!==k.i&&t.i!==u.i&&t.next.i!==u.i&&ff(t,t.next,k,u)){t=!0;break b}t= +t.next}while(t!==k);t=!1}t=!t}if(t=t&&Vc(k,u)&&Vc(u,k)){t=k;r=!1;n=(k.x+u.x)/2;u=(k.y+u.y)/2;do t.y>u!==t.next.y>u&&t.next.y!==t.y&&n<(t.next.x-t.x)*(u-t.y)/(t.next.y-t.y)+t.x&&(r=!r),t=t.next;while(t!==k);t=r}k=t}if(k){a=gf(g,h);g=Tc(g,g.next);a=Tc(a,a.next);Uc(g,b,c,d,e,f);Uc(a,b,c,d,e,f);break a}h=h.next}g=g.next}while(g!==a)}break}}}}function Qg(a,b){return a.x-b.x}function Rg(a,b){var c=b,d=a.x,e=a.y,f=-Infinity;do{if(e<=c.y&&e>=c.next.y&&c.next.y!==c.y){var g=c.x+(e-c.y)*(c.next.x-c.x)/(c.next.y- +c.y);if(g<=d&&g>f){f=g;if(g===d){if(e===c.y)return c;if(e===c.next.y)return c.next}var h=c.x=c.x&&c.x>=g&&d!==c.x&&zd(eh.x)&&Vc(c,a)&&(h=c,m=r)}c=c.next}return h}function ce(a,b,c,d,e){a=32767*(a-c)*e;b=32767*(b-d)*e;a=(a|a<<8)&16711935;a=(a|a<<4)&252645135;a=(a|a<<2)&858993459;b= +(b|b<<8)&16711935;b=(b|b<<4)&252645135;b=(b|b<<2)&858993459;return(a|a<<1)&1431655765|((b|b<<1)&1431655765)<<1}function Sg(a){var b=a,c=a;do b.xna(a.prev,a,a.next)?0<=na(a,b,a.next)&&0<=na(a,a.prev,b):0>na(a,b,a.prev)||0>na(a,a.next,b)}function gf(a,b){var c=new de(a.i,a.x,a.y),d=new de(b.i,b.x,b.y),e=a.next,f=b.prev;a.next=b;b.prev=a;c.next=e;e.prev=c;d.next=c;c.prev=d;f.next=d;d.prev=f;return d}function ef(a,b,c,d){a=new de(a,b,c);d?(a.next=d.next,a.prev=d,d.next.prev=a,d.next=a):(a.prev=a,a.next=a);return a}function Sc(a){a.next.prev=a.prev;a.prev.next=a.next;a.prevZ&&(a.prevZ.nextZ= +a.nextZ);a.nextZ&&(a.nextZ.prevZ=a.prevZ)}function de(a,b,c){this.i=a;this.x=b;this.y=c;this.nextZ=this.prevZ=this.z=this.next=this.prev=null;this.steiner=!1}function hf(a){var b=a.length;2Number.EPSILON){var k=Math.sqrt(h),m=Math.sqrt(f*f+g*g);h=b.x-e/k;b=b.y+d/k;g=((c.x-g/m-h)*g-(c.y+f/m-b)*f)/(d*g-e*f);f=h+d*g-a.x;d=b+e*g-a.y;e=f*f+d*d;if(2>=e)return new A(f,d);e=Math.sqrt(e/2)}else a=!1,d>Number.EPSILON?f>Number.EPSILON&&(a=!0):d<-Number.EPSILON?f<-Number.EPSILON&&(a=!0):Math.sign(e)=== +Math.sign(g)&&(a=!0),a?(f=-e,e=Math.sqrt(h)):(f=d,d=e,e=Math.sqrt(h/2));return new A(f/e,d/e)}function h(a,b){for(O=a.length;0<=--O;){var c=O;var f=O-1;0>f&&(f=a.length-1);var g,h=w+2*B;for(g=0;gr;r++){var n= +m[f[r]];var l=m[f[(r+1)%3]];d[0]=Math.min(n,l);d[1]=Math.max(n,l);n=d[0]+","+d[1];void 0===e[n]?e[n]={index1:d[0],index2:d[1],face1:h,face2:void 0}:e[n].face2=h}for(n in e)if(d=e[n],void 0===d.face2||g[d.face1].normal.dot(g[d.face2].normal)<=b)f=a[d.index1],c.push(f.x,f.y,f.z),f=a[d.index2],c.push(f.x,f.y,f.z);this.addAttribute("position",new z(c,3))}function xb(a,b,c,d,e,f,g,h){M.call(this);this.type="CylinderGeometry";this.parameters={radiusTop:a,radiusBottom:b,height:c,radialSegments:d,heightSegments:e, +openEnded:f,thetaStart:g,thetaLength:h};this.fromBufferGeometry(new $a(a,b,c,d,e,f,g,h));this.mergeVertices()}function $a(a,b,c,d,e,f,g,h){function k(c){var e,f=new A,k=new p,q=0,u=!0===c?a:b,w=!0===c?1:-1;var z=t;for(e=1;e<=d;e++)n.push(0,y*w,0),l.push(0,w,0),v.push(.5,.5),t++;var F=t;for(e=0;e<=d;e++){var C=e/d*h+g,I=Math.cos(C);C=Math.sin(C);k.x=u*C;k.y=y*w;k.z=u*I;n.push(k.x,k.y,k.z);l.push(0,w,0);f.x=.5*I+.5;f.y=.5*C*w+.5;v.push(f.x,f.y);t++}for(e=0;ethis.duration&&this.resetDuration()}function Ug(a){switch(a.toLowerCase()){case "scalar":case "double":case "float":case "number":case "integer":return ic; +case "vector":case "vector2":case "vector3":case "vector4":return jc;case "color":return Md;case "quaternion":return hd;case "bool":case "boolean":return Ld;case "string":return Od}throw Error("THREE.KeyframeTrack: Unsupported typeName: "+a);}function Vg(a){if(void 0===a.type)throw Error("THREE.KeyframeTrack: track type undefined, can not parse");var b=Ug(a.type);if(void 0===a.times){var c=[],d=[];ra.flattenJSON(a.keys,c,d,"value");a.times=c;a.values=d}return void 0!==b.parse?b.parse(a):new b(a.name, +a.times,a.values,a.interpolation)}function Pd(a){this.manager=void 0!==a?a:xa;this.textures={}}function ie(a){this.manager=void 0!==a?a:xa}function kc(){}function je(a){"boolean"===typeof a&&(console.warn("THREE.JSONLoader: showStatus parameter has been removed from constructor."),a=void 0);this.manager=void 0!==a?a:xa;this.withCredentials=!1}function of(a){this.manager=void 0!==a?a:xa;this.texturePath=""}function ke(a){"undefined"===typeof createImageBitmap&&console.warn("THREE.ImageBitmapLoader: createImageBitmap() not supported."); +"undefined"===typeof fetch&&console.warn("THREE.ImageBitmapLoader: fetch() not supported.");this.manager=void 0!==a?a:xa;this.options=void 0}function le(){this.type="ShapePath";this.color=new G;this.subPaths=[];this.currentPath=null}function me(a){this.type="Font";this.data=a}function pf(a){this.manager=void 0!==a?a:xa}function ne(a){this.manager=void 0!==a?a:xa}function qf(){this.type="StereoCamera";this.aspect=1;this.eyeSep=.064;this.cameraL=new V;this.cameraL.layers.enable(1);this.cameraL.matrixAutoUpdate= +!1;this.cameraR=new V;this.cameraR.layers.enable(2);this.cameraR.matrixAutoUpdate=!1}function id(a,b,c){C.call(this);this.type="CubeCamera";var d=new V(90,1,a,b);d.up.set(0,-1,0);d.lookAt(new p(1,0,0));this.add(d);var e=new V(90,1,a,b);e.up.set(0,-1,0);e.lookAt(new p(-1,0,0));this.add(e);var f=new V(90,1,a,b);f.up.set(0,0,1);f.lookAt(new p(0,1,0));this.add(f);var g=new V(90,1,a,b);g.up.set(0,0,-1);g.lookAt(new p(0,-1,0));this.add(g);var h=new V(90,1,a,b);h.up.set(0,-1,0);h.lookAt(new p(0,0,1));this.add(h); +var k=new V(90,1,a,b);k.up.set(0,-1,0);k.lookAt(new p(0,0,-1));this.add(k);this.renderTarget=new Jb(c,c,{format:1022,magFilter:1006,minFilter:1006});this.renderTarget.texture.name="CubeCamera";this.update=function(a,b){null===this.parent&&this.updateMatrixWorld();var c=this.renderTarget,m=c.texture.generateMipmaps;c.texture.generateMipmaps=!1;c.activeCubeFace=0;a.render(b,d,c);c.activeCubeFace=1;a.render(b,e,c);c.activeCubeFace=2;a.render(b,f,c);c.activeCubeFace=3;a.render(b,g,c);c.activeCubeFace= +4;a.render(b,h,c);c.texture.generateMipmaps=m;c.activeCubeFace=5;a.render(b,k,c);a.setRenderTarget(null)};this.clear=function(a,b,c,d){for(var e=this.renderTarget,f=0;6>f;f++)e.activeCubeFace=f,a.setRenderTarget(e),a.clear(b,c,d);a.setRenderTarget(null)}}function oe(){C.call(this);this.type="AudioListener";this.context=pe.getContext();this.gain=this.context.createGain();this.gain.connect(this.context.destination);this.filter=null}function lc(a){C.call(this);this.type="Audio";this.context=a.context; +this.gain=this.context.createGain();this.gain.connect(a.getInput());this.autoplay=!1;this.buffer=null;this.loop=!1;this.offset=this.startTime=0;this.playbackRate=1;this.isPlaying=!1;this.hasPlaybackControl=!0;this.sourceType="empty";this.filters=[]}function qe(a){lc.call(this,a);this.panner=this.context.createPanner();this.panner.connect(this.gain)}function re(a,b){this.analyser=a.context.createAnalyser();this.analyser.fftSize=void 0!==b?b:2048;this.data=new Uint8Array(this.analyser.frequencyBinCount); +a.getOutput().connect(this.analyser)}function se(a,b,c){this.binding=a;this.valueSize=c;a=Float64Array;switch(b){case "quaternion":b=this._slerp;break;case "string":case "bool":a=Array;b=this._select;break;default:b=this._lerp}this.buffer=new a(4*c);this._mixBufferRegion=b;this.referenceCount=this.useCount=this.cumulativeWeight=0}function rf(a,b,c){c=c||ta.parseTrackName(b);this._targetGroup=a;this._bindings=a.subscribe_(b,c)}function ta(a,b,c){this.path=b;this.parsedPath=c||ta.parseTrackName(b); +this.node=ta.findNode(a,this.parsedPath.nodeName)||a;this.rootNode=a}function sf(){this.uuid=S.generateUUID();this._objects=Array.prototype.slice.call(arguments);this.nCachedObjects_=0;var a={};this._indicesByUUID=a;for(var b=0,c=arguments.length;b!==c;++b)a[arguments[b].uuid]=b;this._paths=[];this._parsedPaths=[];this._bindings=[];this._bindingsIndicesByPath={};var d=this;this.stats={objects:{get total(){return d._objects.length},get inUse(){return this.total-d.nCachedObjects_}},get bindingsPerObject(){return d._bindings.length}}} +function tf(a,b,c){this._mixer=a;this._clip=b;this._localRoot=c||null;a=b.tracks;b=a.length;c=Array(b);for(var d={endingStart:2400,endingEnd:2400},e=0;e!==b;++e){var f=a[e].createInterpolant(null);c[e]=f;f.settings=d}this._interpolantSettings=d;this._interpolants=c;this._propertyBindings=Array(b);this._weightInterpolant=this._timeScaleInterpolant=this._byClipCacheIndex=this._cacheIndex=null;this.loop=2201;this._loopCount=-1;this._startTime=null;this.time=0;this._effectiveWeight=this.weight=this._effectiveTimeScale= +this.timeScale=1;this.repetitions=Infinity;this.paused=!1;this.enabled=!0;this.clampWhenFinished=!1;this.zeroSlopeAtEnd=this.zeroSlopeAtStart=!0}function te(a){this._root=a;this._initMemoryManager();this.time=this._accuIndex=0;this.timeScale=1}function Qd(a,b){"string"===typeof a&&(console.warn("THREE.Uniform: Type parameter is no longer needed."),a=b);this.value=a}function ue(){F.call(this);this.type="InstancedBufferGeometry";this.maxInstancedCount=void 0}function ve(a,b,c){qb.call(this,a,b);this.meshPerAttribute= +c||1}function we(a,b,c,d){"number"===typeof c&&(d=c,c=!1,console.error("THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument."));I.call(this,a,b,c);this.meshPerAttribute=d||1}function uf(a,b,c,d){this.ray=new pb(a,b);this.near=c||0;this.far=d||Infinity;this.params={Mesh:{},Line:{},LOD:{},Points:{threshold:1},Sprite:{}};Object.defineProperties(this.params,{PointCloud:{get:function(){console.warn("THREE.Raycaster: params.PointCloud has been renamed to params.Points."); +return this.Points}}})}function vf(a,b){return a.distance-b.distance}function xe(a,b,c,d){if(!1!==a.visible&&(a.raycast(b,c),!0===d)){a=a.children;d=0;for(var e=a.length;dc;c++,d++){var e=c/32*Math.PI*2,f=d/32*Math.PI*2;b.push(Math.cos(e),Math.sin(e),1,Math.cos(f),Math.sin(f),1)}a.addAttribute("position",new z(b,3));b=new T({fog:!1}); +this.cone=new W(a,b);this.add(this.cone);this.update()}function zf(a){var b=[];a&&a.isBone&&b.push(a);for(var c=0;ca?-1:0b;b++)a[b]=(16>b?"0":"")+b.toString(16); +return function(){var b=4294967295*Math.random()|0,d=4294967295*Math.random()|0,e=4294967295*Math.random()|0,f=4294967295*Math.random()|0;return(a[b&255]+a[b>>8&255]+a[b>>16&255]+a[b>>24&255]+"-"+a[d&255]+a[d>>8&255]+"-"+a[d>>16&15|64]+a[d>>24&255]+"-"+a[e&63|128]+a[e>>8&255]+"-"+a[e>>16&255]+a[e>>24&255]+a[f&255]+a[f>>8&255]+a[f>>16&255]+a[f>>24&255]).toUpperCase()}}(),clamp:function(a,b,c){return Math.max(b,Math.min(c,a))},euclideanModulo:function(a,b){return(a%b+b)%b},mapLinear:function(a,b,c, +d,e){return d+(a-b)*(e-d)/(c-b)},lerp:function(a,b,c){return(1-c)*a+c*b},smoothstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*(3-2*a)},smootherstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*a*(a*(6*a-15)+10)},randInt:function(a,b){return a+Math.floor(Math.random()*(b-a+1))},randFloat:function(a,b){return a+Math.random()*(b-a)},randFloatSpread:function(a){return a*(.5-Math.random())},degToRad:function(a){return a*S.DEG2RAD},radToDeg:function(a){return a* +S.RAD2DEG},isPowerOfTwo:function(a){return 0===(a&a-1)&&0!==a},ceilPowerOfTwo:function(a){return Math.pow(2,Math.ceil(Math.log(a)/Math.LN2))},floorPowerOfTwo:function(a){return Math.pow(2,Math.floor(Math.log(a)/Math.LN2))}};Object.defineProperties(A.prototype,{width:{get:function(){return this.x},set:function(a){this.x=a}},height:{get:function(){return this.y},set:function(a){this.y=a}}});Object.assign(A.prototype,{isVector2:!0,set:function(a,b){this.x=a;this.y=b;return this},setScalar:function(a){this.y= +this.x=a;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;default:throw Error("index is out of range: "+a);}return this},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;default:throw Error("index is out of range: "+a);}},clone:function(){return new this.constructor(this.x,this.y)},copy:function(a){this.x=a.x;this.y=a.y;return this},add:function(a, +b){if(void 0!==b)return console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;return this},addScalar:function(a){this.x+=a;this.y+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;return this},addScaledVector:function(a,b){this.x+=a.x*b;this.y+=a.y*b;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."), +this.subVectors(a,b);this.x-=a.x;this.y-=a.y;return this},subScalar:function(a){this.x-=a;this.y-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;return this},multiply:function(a){this.x*=a.x;this.y*=a.y;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;return this},divide:function(a){this.x/=a.x;this.y/=a.y;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},applyMatrix3:function(a){var b=this.x,c=this.y;a=a.elements;this.x=a[0]*b+a[3]*c+a[6];this.y= +a[1]*b+a[4]*c+a[7];return this},min:function(a){this.x=Math.min(this.x,a.x);this.y=Math.min(this.y,a.y);return this},max:function(a){this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);return this},clamp:function(a,b){this.x=Math.max(a.x,Math.min(b.x,this.x));this.y=Math.max(a.y,Math.min(b.y,this.y));return this},clampScalar:function(){var a=new A,b=new A;return function(c,d){a.set(c,c);b.set(d,d);return this.clamp(a,b)}}(),clampLength:function(a,b){var c=this.length();return this.divideScalar(c|| +1).multiplyScalar(Math.max(a,Math.min(b,c)))},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=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);return this},negate:function(){this.x=-this.x;this.y=-this.y;return this},dot:function(a){return this.x* +a.x+this.y*a.y},cross:function(a){return this.x*a.y-this.y*a.x},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(){var a=Math.atan2(this.y,this.x);0>a&&(a+=2*Math.PI);return a},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b= +this.x-a.x;a=this.y-a.y;return b*b+a*a},manhattanDistanceTo:function(a){return Math.abs(this.x-a.x)+Math.abs(this.y-a.y)},setLength:function(a){return this.normalize().multiplyScalar(a)},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;return this},lerpVectors:function(a,b,c){return this.subVectors(b,a).multiplyScalar(c).add(a)},equals:function(a){return a.x===this.x&&a.y===this.y},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];return this},toArray:function(a, +b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;return a},fromBufferAttribute:function(a,b,c){void 0!==c&&console.warn("THREE.Vector2: offset has been removed from .fromBufferAttribute().");this.x=a.getX(b);this.y=a.getY(b);return this},rotateAround:function(a,b){var c=Math.cos(b);b=Math.sin(b);var d=this.x-a.x,e=this.y-a.y;this.x=d*c-e*b+a.x;this.y=d*b+e*c+a.y;return this}});Object.assign(P.prototype,{isMatrix4:!0,set:function(a,b,c,d,e,f,g,h,k,m,r,n,l,v,t,p){var q=this.elements; +q[0]=a;q[4]=b;q[8]=c;q[12]=d;q[1]=e;q[5]=f;q[9]=g;q[13]=h;q[2]=k;q[6]=m;q[10]=r;q[14]=n;q[3]=l;q[7]=v;q[11]=t;q[15]=p;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 P).fromArray(this.elements)},copy:function(a){var b=this.elements;a=a.elements;b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=a[12];b[13]=a[13];b[14]=a[14];b[15]=a[15];return this},copyPosition:function(a){var b= +this.elements;a=a.elements;b[12]=a[12];b[13]=a[13];b[14]=a[14];return this},extractBasis:function(a,b,c){a.setFromMatrixColumn(this,0);b.setFromMatrixColumn(this,1);c.setFromMatrixColumn(this,2);return this},makeBasis:function(a,b,c){this.set(a.x,b.x,c.x,0,a.y,b.y,c.y,0,a.z,b.z,c.z,0,0,0,0,1);return this},extractRotation:function(){var a=new p;return function(b){var c=this.elements,d=b.elements,e=1/a.setFromMatrixColumn(b,0).length(),f=1/a.setFromMatrixColumn(b,1).length();b=1/a.setFromMatrixColumn(b, +2).length();c[0]=d[0]*e;c[1]=d[1]*e;c[2]=d[2]*e;c[3]=0;c[4]=d[4]*f;c[5]=d[5]*f;c[6]=d[6]*f;c[7]=0;c[8]=d[8]*b;c[9]=d[9]*b;c[10]=d[10]*b;c[11]=0;c[12]=0;c[13]=0;c[14]=0;c[15]=1;return this}}(),makeRotationFromEuler:function(a){a&&a.isEuler||console.error("THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var b=this.elements,c=a.x,d=a.y,e=a.z,f=Math.cos(c);c=Math.sin(c);var g=Math.cos(d);d=Math.sin(d);var h=Math.cos(e);e=Math.sin(e);if("XYZ"===a.order){a= +f*h;var k=f*e,m=c*h,r=c*e;b[0]=g*h;b[4]=-g*e;b[8]=d;b[1]=k+m*d;b[5]=a-r*d;b[9]=-c*g;b[2]=r-a*d;b[6]=m+k*d;b[10]=f*g}else"YXZ"===a.order?(a=g*h,k=g*e,m=d*h,r=d*e,b[0]=a+r*c,b[4]=m*c-k,b[8]=f*d,b[1]=f*e,b[5]=f*h,b[9]=-c,b[2]=k*c-m,b[6]=r+a*c,b[10]=f*g):"ZXY"===a.order?(a=g*h,k=g*e,m=d*h,r=d*e,b[0]=a-r*c,b[4]=-f*e,b[8]=m+k*c,b[1]=k+m*c,b[5]=f*h,b[9]=r-a*c,b[2]=-f*d,b[6]=c,b[10]=f*g):"ZYX"===a.order?(a=f*h,k=f*e,m=c*h,r=c*e,b[0]=g*h,b[4]=m*d-k,b[8]=a*d+r,b[1]=g*e,b[5]=r*d+a,b[9]=k*d-m,b[2]=-d,b[6]=c* +g,b[10]=f*g):"YZX"===a.order?(a=f*g,k=f*d,m=c*g,r=c*d,b[0]=g*h,b[4]=r-a*e,b[8]=m*e+k,b[1]=e,b[5]=f*h,b[9]=-c*h,b[2]=-d*h,b[6]=k*e+m,b[10]=a-r*e):"XZY"===a.order&&(a=f*g,k=f*d,m=c*g,r=c*d,b[0]=g*h,b[4]=-e,b[8]=d*h,b[1]=a*e+r,b[5]=f*h,b[9]=k*e-m,b[2]=m*e-k,b[6]=c*h,b[10]=r*e+a);b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},makeRotationFromQuaternion:function(){var a=new p(0,0,0),b=new p(1,1,1);return function(c){return this.compose(a,c,b)}}(),lookAt:function(){var a=new p,b=new p, +c=new p;return function(d,e,f){var g=this.elements;c.subVectors(d,e);0===c.lengthSq()&&(c.z=1);c.normalize();a.crossVectors(f,c);0===a.lengthSq()&&(1===Math.abs(f.z)?c.x+=1E-4:c.z+=1E-4,c.normalize(),a.crossVectors(f,c));a.normalize();b.crossVectors(c,a);g[0]=a.x;g[4]=b.x;g[8]=c.x;g[1]=a.y;g[5]=b.y;g[9]=c.y;g[2]=a.z;g[6]=b.z;g[10]=c.z;return this}}(),multiply:function(a,b){return void 0!==b?(console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."), +this.multiplyMatrices(a,b)):this.multiplyMatrices(this,a)},premultiply:function(a){return this.multiplyMatrices(a,this)},multiplyMatrices:function(a,b){var c=a.elements,d=b.elements;b=this.elements;a=c[0];var e=c[4],f=c[8],g=c[12],h=c[1],k=c[5],m=c[9],r=c[13],n=c[2],l=c[6],v=c[10],t=c[14],p=c[3],y=c[7],x=c[11];c=c[15];var w=d[0],D=d[4],J=d[8],R=d[12],A=d[1],B=d[5],z=d[9],C=d[13],F=d[2],I=d[6],G=d[10],H=d[14],L=d[3],M=d[7],K=d[11];d=d[15];b[0]=a*w+e*A+f*F+g*L;b[4]=a*D+e*B+f*I+g*M;b[8]=a*J+e*z+f*G+ +g*K;b[12]=a*R+e*C+f*H+g*d;b[1]=h*w+k*A+m*F+r*L;b[5]=h*D+k*B+m*I+r*M;b[9]=h*J+k*z+m*G+r*K;b[13]=h*R+k*C+m*H+r*d;b[2]=n*w+l*A+v*F+t*L;b[6]=n*D+l*B+v*I+t*M;b[10]=n*J+l*z+v*G+t*K;b[14]=n*R+l*C+v*H+t*d;b[3]=p*w+y*A+x*F+c*L;b[7]=p*D+y*B+x*I+c*M;b[11]=p*J+y*z+x*G+c*K;b[15]=p*R+y*C+x*H+c*d;return this},multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[4]*=a;b[8]*=a;b[12]*=a;b[1]*=a;b[5]*=a;b[9]*=a;b[13]*=a;b[2]*=a;b[6]*=a;b[10]*=a;b[14]*=a;b[3]*=a;b[7]*=a;b[11]*=a;b[15]*=a;return this},applyToBufferAttribute:function(){var a= +new p;return function(b){for(var c=0,d=b.count;cthis.determinant()&&(g=-g);c.x=f[12];c.y=f[13];c.z=f[14];b.copy(this);c=1/g;f=1/h;var m=1/k;b.elements[0]*=c;b.elements[1]*=c;b.elements[2]*=c;b.elements[4]*=f;b.elements[5]*=f;b.elements[6]*=f;b.elements[8]*=m;b.elements[9]*=m;b.elements[10]*=m;d.setFromRotationMatrix(b);e.x=g;e.y=h;e.z=k;return this}}(),makePerspective:function(a,b,c,d,e,f){void 0===f&&console.warn("THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs."); +var g=this.elements;g[0]=2*e/(b-a);g[4]=0;g[8]=(b+a)/(b-a);g[12]=0;g[1]=0;g[5]=2*e/(c-d);g[9]=(c+d)/(c-d);g[13]=0;g[2]=0;g[6]=0;g[10]=-(f+e)/(f-e);g[14]=-2*f*e/(f-e);g[3]=0;g[7]=0;g[11]=-1;g[15]=0;return this},makeOrthographic:function(a,b,c,d,e,f){var g=this.elements,h=1/(b-a),k=1/(c-d),m=1/(f-e);g[0]=2*h;g[4]=0;g[8]=0;g[12]=-((b+a)*h);g[1]=0;g[5]=2*k;g[9]=0;g[13]=-((c+d)*k);g[2]=0;g[6]=0;g[10]=-2*m;g[14]=-((f+e)*m);g[3]=0;g[7]=0;g[11]=0;g[15]=1;return this},equals:function(a){var b=this.elements; +a=a.elements;for(var c=0;16>c;c++)if(b[c]!==a[c])return!1;return!0},fromArray:function(a,b){void 0===b&&(b=0);for(var c=0;16>c;c++)this.elements[c]=a[c+b];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];a[b+9]=c[9];a[b+10]=c[10];a[b+11]=c[11];a[b+12]=c[12];a[b+13]=c[13];a[b+14]=c[14];a[b+15]=c[15];return a}});Object.assign(ja,{isQuaternion:!0,slerp:function(a, +b,c,d){return c.copy(a).slerp(b,d)},slerpFlat:function(a,b,c,d,e,f,g){var h=c[d+0],k=c[d+1],m=c[d+2];c=c[d+3];d=e[f+0];var l=e[f+1],n=e[f+2];e=e[f+3];if(c!==e||h!==d||k!==l||m!==n){f=1-g;var q=h*d+k*l+m*n+c*e,v=0<=q?1:-1,p=1-q*q;p>Number.EPSILON&&(p=Math.sqrt(p),q=Math.atan2(p,q*v),f=Math.sin(f*q)/p,g=Math.sin(g*q)/p);v*=g;h=h*f+d*v;k=k*f+l*v;m=m*f+n*v;c=c*f+e*v;f===1-g&&(g=1/Math.sqrt(h*h+k*k+m*m+c*c),h*=g,k*=g,m*=g,c*=g)}a[b]=h;a[b+1]=k;a[b+2]=m;a[b+3]=c}});Object.defineProperties(ja.prototype, +{x:{get:function(){return this._x},set:function(a){this._x=a;this.onChangeCallback()}},y:{get:function(){return this._y},set:function(a){this._y=a;this.onChangeCallback()}},z:{get:function(){return this._z},set:function(a){this._z=a;this.onChangeCallback()}},w:{get:function(){return this._w},set:function(a){this._w=a;this.onChangeCallback()}}});Object.assign(ja.prototype,{set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._w=d;this.onChangeCallback();return this},clone:function(){return new this.constructor(this._x, +this._y,this._z,this._w)},copy:function(a){this._x=a.x;this._y=a.y;this._z=a.z;this._w=a.w;this.onChangeCallback();return this},setFromEuler:function(a,b){if(!a||!a.isEuler)throw Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.");var c=a._x,d=a._y,e=a._z;a=a.order;var f=Math.cos,g=Math.sin,h=f(c/2),k=f(d/2);f=f(e/2);c=g(c/2);d=g(d/2);e=g(e/2);"XYZ"===a?(this._x=c*k*f+h*d*e,this._y=h*d*f-c*k*e,this._z=h*k*e+c*d*f,this._w=h*k*f-c*d*e):"YXZ"===a? +(this._x=c*k*f+h*d*e,this._y=h*d*f-c*k*e,this._z=h*k*e-c*d*f,this._w=h*k*f+c*d*e):"ZXY"===a?(this._x=c*k*f-h*d*e,this._y=h*d*f+c*k*e,this._z=h*k*e+c*d*f,this._w=h*k*f-c*d*e):"ZYX"===a?(this._x=c*k*f-h*d*e,this._y=h*d*f+c*k*e,this._z=h*k*e-c*d*f,this._w=h*k*f+c*d*e):"YZX"===a?(this._x=c*k*f+h*d*e,this._y=h*d*f+c*k*e,this._z=h*k*e-c*d*f,this._w=h*k*f-c*d*e):"XZY"===a&&(this._x=c*k*f-h*d*e,this._y=h*d*f-c*k*e,this._z=h*k*e+c*d*f,this._w=h*k*f+c*d*e);if(!1!==b)this.onChangeCallback();return this},setFromAxisAngle:function(a, +b){b/=2;var c=Math.sin(b);this._x=a.x*c;this._y=a.y*c;this._z=a.z*c;this._w=Math.cos(b);this.onChangeCallback();return this},setFromRotationMatrix:function(a){var b=a.elements,c=b[0];a=b[4];var d=b[8],e=b[1],f=b[5],g=b[9],h=b[2],k=b[6];b=b[10];var m=c+f+b;0f&&c>b?(c=2*Math.sqrt(1+c-f-b),this._w=(k-g)/c,this._x=.25*c,this._y=(a+e)/c,this._z=(d+h)/c):f>b?(c=2*Math.sqrt(1+f-c-b),this._w=(d-h)/c,this._x=(a+e)/c,this._y= +.25*c,this._z=(g+k)/c):(c=2*Math.sqrt(1+b-c-f),this._w=(e-a)/c,this._x=(d+h)/c,this._y=(g+k)/c,this._z=.25*c);this.onChangeCallback();return this},setFromUnitVectors:function(){var a=new p,b;return function(c,d){void 0===a&&(a=new p);b=c.dot(d)+1;1E-6>b?(b=0,Math.abs(c.x)>Math.abs(c.z)?a.set(-c.y,c.x,0):a.set(0,-c.z,c.y)):a.crossVectors(c,d);this._x=a.x;this._y=a.y;this._z=a.z;this._w=b;return this.normalize()}}(),angleTo:function(a){return 2*Math.acos(Math.abs(S.clamp(this.dot(a),-1,1)))},rotateTowards:function(a, +b){var c=this.angleTo(a);if(0===c)return this;this.slerp(a,Math.min(1,b/c));return this},inverse:function(){return this.conjugate()},conjugate:function(){this._x*=-1;this._y*=-1;this._z*=-1;this.onChangeCallback();return this},dot:function(a){return this._x*a._x+this._y*a._y+this._z*a._z+this._w*a._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 a= +this.length();0===a?(this._z=this._y=this._x=0,this._w=1):(a=1/a,this._x*=a,this._y*=a,this._z*=a,this._w*=a);this.onChangeCallback();return this},multiply:function(a,b){return void 0!==b?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(a,b)):this.multiplyQuaternions(this,a)},premultiply:function(a){return this.multiplyQuaternions(a,this)},multiplyQuaternions:function(a,b){var c=a._x,d=a._y,e=a._z;a=a._w; +var f=b._x,g=b._y,h=b._z;b=b._w;this._x=c*b+a*f+d*h-e*g;this._y=d*b+a*g+e*f-c*h;this._z=e*b+a*h+c*g-d*f;this._w=a*b-c*f-d*g-e*h;this.onChangeCallback();return this},slerp:function(a,b){if(0===b)return this;if(1===b)return this.copy(a);var c=this._x,d=this._y,e=this._z,f=this._w,g=f*a._w+c*a._x+d*a._y+e*a._z;0>g?(this._w=-a._w,this._x=-a._x,this._y=-a._y,this._z=-a._z,g=-g):this.copy(a);if(1<=g)return this._w=f,this._x=c,this._y=d,this._z=e,this;a=1-g*g;if(a<=Number.EPSILON)return g=1-b,this._w=g* +f+b*this._w,this._x=g*c+b*this._x,this._y=g*d+b*this._y,this._z=g*e+b*this._z,this.normalize();a=Math.sqrt(a);var h=Math.atan2(a,g);g=Math.sin((1-b)*h)/a;b=Math.sin(b*h)/a;this._w=f*g+this._w*b;this._x=c*g+this._x*b;this._y=d*g+this._y*b;this._z=e*g+this._z*b;this.onChangeCallback();return this},equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._w===this._w},fromArray:function(a,b){void 0===b&&(b=0);this._x=a[b];this._y=a[b+1];this._z=a[b+2];this._w=a[b+3];this.onChangeCallback(); +return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this._x;a[b+1]=this._y;a[b+2]=this._z;a[b+3]=this._w;return a},onChange:function(a){this.onChangeCallback=a;return this},onChangeCallback:function(){}});Object.assign(p.prototype,{isVector3:!0,set:function(a,b,c){this.x=a;this.y=b;this.z=c;return this},setScalar:function(a){this.z=this.y=this.x=a;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this}, +setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;default:throw Error("index is out of range: "+a);}return this},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw Error("index is out of range: "+a);}},clone:function(){return new this.constructor(this.x,this.y,this.z)},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;return this},add:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."), +this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;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(a,b){this.x+=a.x*b;this.y+=a.y*b;this.z+=a.z*b;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z; +return this},subScalar:function(a){this.x-=a;this.y-=a;this.z-=a;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(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(a,b);this.x*=a.x;this.y*=a.y;this.z*=a.z;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;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 a=new ja;return function(b){b&&b.isEuler||console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.");return this.applyQuaternion(a.setFromEuler(b))}}(),applyAxisAngle:function(){var a=new ja;return function(b,c){return this.applyQuaternion(a.setFromAxisAngle(b,c))}}(),applyMatrix3:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[3]*c+a[6]*d;this.y=a[1]* +b+a[4]*c+a[7]*d;this.z=a[2]*b+a[5]*c+a[8]*d;return this},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;var e=1/(a[3]*b+a[7]*c+a[11]*d+a[15]);this.x=(a[0]*b+a[4]*c+a[8]*d+a[12])*e;this.y=(a[1]*b+a[5]*c+a[9]*d+a[13])*e;this.z=(a[2]*b+a[6]*c+a[10]*d+a[14])*e;return this},applyQuaternion:function(a){var b=this.x,c=this.y,d=this.z,e=a.x,f=a.y,g=a.z;a=a.w;var h=a*b+f*d-g*c,k=a*c+g*b-e*d,m=a*d+e*c-f*b;b=-e*b-f*c-g*d;this.x=h*a+b*-e+k*-g-m*-f;this.y=k*a+b*-f+m*-e-h*-g;this.z=m*a+b* +-g+h*-f-k*-e;return this},project:function(a){return this.applyMatrix4(a.matrixWorldInverse).applyMatrix4(a.projectionMatrix)},unproject:function(){var a=new P;return function(b){return this.applyMatrix4(a.getInverse(b.projectionMatrix)).applyMatrix4(b.matrixWorld)}}(),transformDirection:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d;this.y=a[1]*b+a[5]*c+a[9]*d;this.z=a[2]*b+a[6]*c+a[10]*d;return this.normalize()},divide:function(a){this.x/=a.x;this.y/=a.y;this.z/= +a.z;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},min:function(a){this.x=Math.min(this.x,a.x);this.y=Math.min(this.y,a.y);this.z=Math.min(this.z,a.z);return this},max:function(a){this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);this.z=Math.max(this.z,a.z);return this},clamp:function(a,b){this.x=Math.max(a.x,Math.min(b.x,this.x));this.y=Math.max(a.y,Math.min(b.y,this.y));this.z=Math.max(a.z,Math.min(b.z,this.z));return this},clampScalar:function(){var a=new p,b=new p; +return function(c,d){a.set(c,c,c);b.set(d,d,d);return this.clamp(a,b)}}(),clampLength:function(a,b){var c=this.length();return this.divideScalar(c||1).multiplyScalar(Math.max(a,Math.min(b,c)))},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=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);this.z=0>this.z?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(a){return this.x*a.x+this.y*a.y+this.z*a.z},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(a){return this.normalize().multiplyScalar(a)},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;return this},lerpVectors:function(a,b,c){return this.subVectors(b,a).multiplyScalar(c).add(a)},cross:function(a,b){return void 0!==b?(console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(a, +b)):this.crossVectors(this,a)},crossVectors:function(a,b){var c=a.x,d=a.y;a=a.z;var e=b.x,f=b.y;b=b.z;this.x=d*b-a*f;this.y=a*e-c*b;this.z=c*f-d*e;return this},projectOnVector:function(a){var b=a.dot(this)/a.lengthSq();return this.copy(a).multiplyScalar(b)},projectOnPlane:function(){var a=new p;return function(b){a.copy(this).projectOnVector(b);return this.sub(a)}}(),reflect:function(){var a=new p;return function(b){return this.sub(a.copy(b).multiplyScalar(2*this.dot(b)))}}(),angleTo:function(a){a= +this.dot(a)/Math.sqrt(this.lengthSq()*a.lengthSq());return Math.acos(S.clamp(a,-1,1))},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x,c=this.y-a.y;a=this.z-a.z;return b*b+c*c+a*a},manhattanDistanceTo:function(a){return Math.abs(this.x-a.x)+Math.abs(this.y-a.y)+Math.abs(this.z-a.z)},setFromSpherical:function(a){return this.setFromSphericalCoords(a.radius,a.phi,a.theta)},setFromSphericalCoords:function(a,b,c){var d=Math.sin(b)*a;this.x= +d*Math.sin(c);this.y=Math.cos(b)*a;this.z=d*Math.cos(c);return this},setFromCylindrical:function(a){return this.setFromCylindricalCoords(a.radius,a.theta,a.y)},setFromCylindricalCoords:function(a,b,c){this.x=a*Math.sin(b);this.y=c;this.z=a*Math.cos(b);return this},setFromMatrixPosition:function(a){a=a.elements;this.x=a[12];this.y=a[13];this.z=a[14];return this},setFromMatrixScale:function(a){var b=this.setFromMatrixColumn(a,0).length(),c=this.setFromMatrixColumn(a,1).length();a=this.setFromMatrixColumn(a, +2).length();this.x=b;this.y=c;this.z=a;return this},setFromMatrixColumn:function(a,b){return this.fromArray(a.elements,4*b)},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=this.z;return a},fromBufferAttribute:function(a,b,c){void 0!==c&&console.warn("THREE.Vector3: offset has been removed from .fromBufferAttribute()."); +this.x=a.getX(b);this.y=a.getY(b);this.z=a.getZ(b);return this}});Object.assign(oa.prototype,{isMatrix3:!0,set:function(a,b,c,d,e,f,g,h,k){var m=this.elements;m[0]=a;m[1]=d;m[2]=g;m[3]=b;m[4]=e;m[5]=h;m[6]=c;m[7]=f;m[8]=k;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(a){var b=this.elements;a=a.elements;b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]= +a[8];return this},setFromMatrix4:function(a){a=a.elements;this.set(a[0],a[4],a[8],a[1],a[5],a[9],a[2],a[6],a[10]);return this},applyToBufferAttribute:function(){var a=new p;return function(b){for(var c=0,d=b.count;cc;c++)if(b[c]!==a[c])return!1;return!0},fromArray:function(a,b){void 0===b&&(b=0);for(var c=0;9>c;c++)this.elements[c]=a[c+b];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8]; +return a}});var hb={getDataURL:function(a){if(a instanceof HTMLCanvasElement)var b=a;else{b=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");b.width=a.width;b.height=a.height;var c=b.getContext("2d");a instanceof ImageData?c.putImageData(a,0,0):c.drawImage(a,0,0,a.width,a.height)}return 2048a.x||1a.x?0:1;break;case 1002:a.x=1===Math.abs(Math.floor(a.x)%2)?Math.ceil(a.x)-a.x:a.x-Math.floor(a.x)}if(0>a.y||1a.y?0:1;break;case 1002:a.y=1===Math.abs(Math.floor(a.y)%2)?Math.ceil(a.y)-a.y:a.y-Math.floor(a.y)}this.flipY&&(a.y=1-a.y);return a}});Object.defineProperty(Q.prototype,"needsUpdate",{set:function(a){!0===a&&this.version++}});Object.assign(Z.prototype,{isVector4:!0,set:function(a,b,c,d){this.x=a;this.y=b;this.z=c;this.w=d;return this},setScalar:function(a){this.w=this.z=this.y=this.x=a;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y= +a;return this},setZ:function(a){this.z=a;return this},setW:function(a){this.w=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;case 3:this.w=b;break;default:throw Error("index is out of range: "+a);}return this},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw Error("index is out of range: "+a);}},clone:function(){return new this.constructor(this.x, +this.y,this.z,this.w)},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;this.w=void 0!==a.w?a.w:1;return this},add:function(a,b){if(void 0!==b)return console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;this.w+=a.w;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;this.w+=a;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(a,b){this.x+=a.x*b;this.y+=a.y*b;this.z+=a.z*b;this.w+=a.w*b;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;this.w-=a.w;return this},subScalar:function(a){this.x-=a;this.y-=a;this.z-=a;this.w-=a;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(a){this.x*= +a;this.y*=a;this.z*=a;this.w*=a;return this},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z,e=this.w;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12]*e;this.y=a[1]*b+a[5]*c+a[9]*d+a[13]*e;this.z=a[2]*b+a[6]*c+a[10]*d+a[14]*e;this.w=a[3]*b+a[7]*c+a[11]*d+a[15]*e;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},setAxisAngleFromQuaternion:function(a){this.w=2*Math.acos(a.w);var b=Math.sqrt(1-a.w*a.w);1E-4>b?(this.x=1,this.z=this.y=0):(this.x=a.x/b,this.y=a.y/b,this.z=a.z/ +b);return this},setAxisAngleFromRotationMatrix:function(a){a=a.elements;var b=a[0];var c=a[4];var d=a[8],e=a[1],f=a[5],g=a[9];var h=a[2];var k=a[6];var m=a[10];if(.01>Math.abs(c-e)&&.01>Math.abs(d-h)&&.01>Math.abs(g-k)){if(.1>Math.abs(c+e)&&.1>Math.abs(d+h)&&.1>Math.abs(g+k)&&.1>Math.abs(b+f+m-3))return this.set(1,0,0,0),this;a=Math.PI;b=(b+1)/2;f=(f+1)/2;m=(m+1)/2;c=(c+e)/4;d=(d+h)/4;g=(g+k)/4;b>f&&b>m?.01>b?(k=0,c=h=.707106781):(k=Math.sqrt(b),h=c/k,c=d/k):f>m?.01>f?(k=.707106781,h=0,c=.707106781): +(h=Math.sqrt(f),k=c/h,c=g/h):.01>m?(h=k=.707106781,c=0):(c=Math.sqrt(m),k=d/c,h=g/c);this.set(k,h,c,a);return this}a=Math.sqrt((k-g)*(k-g)+(d-h)*(d-h)+(e-c)*(e-c));.001>Math.abs(a)&&(a=1);this.x=(k-g)/a;this.y=(d-h)/a;this.z=(e-c)/a;this.w=Math.acos((b+f+m-1)/2);return this},min:function(a){this.x=Math.min(this.x,a.x);this.y=Math.min(this.y,a.y);this.z=Math.min(this.z,a.z);this.w=Math.min(this.w,a.w);return this},max:function(a){this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);this.z=Math.max(this.z, +a.z);this.w=Math.max(this.w,a.w);return this},clamp:function(a,b){this.x=Math.max(a.x,Math.min(b.x,this.x));this.y=Math.max(a.y,Math.min(b.y,this.y));this.z=Math.max(a.z,Math.min(b.z,this.z));this.w=Math.max(a.w,Math.min(b.w,this.w));return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new Z,b=new Z);a.set(c,c,c,c);b.set(d,d,d,d);return this.clamp(a,b)}}(),clampLength:function(a,b){var c=this.length();return this.divideScalar(c||1).multiplyScalar(Math.max(a,Math.min(b,c)))}, +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=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y): +Math.floor(this.y);this.z=0>this.z?Math.ceil(this.z):Math.floor(this.z);this.w=0>this.w?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(a){return this.x*a.x+this.y*a.y+this.z*a.z+this.w*a.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(a){return this.normalize().multiplyScalar(a)},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;this.w+=(a.w-this.w)*b;return this},lerpVectors:function(a,b,c){return this.subVectors(b,a).multiplyScalar(c).add(a)},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z&&a.w===this.w},fromArray:function(a,b){void 0=== +b&&(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];this.w=a[b+3];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=this.z;a[b+3]=this.w;return a},fromBufferAttribute:function(a,b,c){void 0!==c&&console.warn("THREE.Vector4: offset has been removed from .fromBufferAttribute().");this.x=a.getX(b);this.y=a.getY(b);this.z=a.getZ(b);this.w=a.getW(b);return this}});ib.prototype=Object.assign(Object.create(ia.prototype),{constructor:ib,isWebGLRenderTarget:!0, +setSize:function(a,b){if(this.width!==a||this.height!==b)this.width=a,this.height=b,this.dispose();this.viewport.set(0,0,a,b);this.scissor.set(0,0,a,b)},clone:function(){return(new this.constructor).copy(this)},copy:function(a){this.width=a.width;this.height=a.height;this.viewport.copy(a.viewport);this.texture=a.texture.clone();this.depthBuffer=a.depthBuffer;this.stencilBuffer=a.stencilBuffer;this.depthTexture=a.depthTexture;return this},dispose:function(){this.dispatchEvent({type:"dispose"})}}); +Jb.prototype=Object.create(ib.prototype);Jb.prototype.constructor=Jb;Jb.prototype.isWebGLRenderTargetCube=!0;jb.prototype=Object.create(Q.prototype);jb.prototype.constructor=jb;jb.prototype.isDataTexture=!0;Object.assign(Wa.prototype,{isBox3:!0,set:function(a,b){this.min.copy(a);this.max.copy(b);return this},setFromArray:function(a){for(var b=Infinity,c=Infinity,d=Infinity,e=-Infinity,f=-Infinity,g=-Infinity,h=0,k=a.length;h +e&&(e=m);l>f&&(f=l);n>g&&(g=n)}this.min.set(b,c,d);this.max.set(e,f,g);return this},setFromBufferAttribute:function(a){for(var b=Infinity,c=Infinity,d=Infinity,e=-Infinity,f=-Infinity,g=-Infinity,h=0,k=a.count;he&&(e=m);l>f&&(f=l);n>g&&(g=n)}this.min.set(b,c,d);this.max.set(e,f,g);return this},setFromPoints:function(a){this.makeEmpty();for(var b=0,c=a.length;bthis.max.x||a.ythis.max.y||a.zthis.max.z?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y&&this.min.z<=a.min.z&&a.max.z<=this.max.z},getParameter:function(a,b){void 0===b&&(console.warn("THREE.Box3: .getParameter() target is now required"),b=new p);return b.set((a.x-this.min.x)/(this.max.x- +this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y),(a.z-this.min.z)/(this.max.z-this.min.z))},intersectsBox:function(a){return a.max.xthis.max.x||a.max.ythis.max.y||a.max.zthis.max.z?!1:!0},intersectsSphere:function(){var a=new p;return function(b){this.clampPoint(b.center,a);return a.distanceToSquared(b.center)<=b.radius*b.radius}}(),intersectsPlane:function(a){if(0=-a.constant},intersectsTriangle:function(){function a(a){var e;var f=0;for(e=a.length-3;f<=e;f+=3){h.fromArray(a,f);var g=m.x*Math.abs(h.x)+m.y*Math.abs(h.y)+m.z*Math.abs(h.z),k=b.dot(h),l=c.dot(h), +n=d.dot(h);if(Math.max(-Math.max(k,l,n),Math.min(k,l,n))>g)return!1}return!0}var b=new p,c=new p,d=new p,e=new p,f=new p,g=new p,h=new p,k=new p,m=new p,l=new p;return function(h){if(this.isEmpty())return!1;this.getCenter(k);m.subVectors(this.max,k);b.subVectors(h.a,k);c.subVectors(h.b,k);d.subVectors(h.c,k);e.subVectors(c,b);f.subVectors(d,c);g.subVectors(b,d);h=[0,-e.z,e.y,0,-f.z,f.y,0,-g.z,g.y,e.z,0,-e.x,f.z,0,-f.x,g.z,0,-g.x,-e.y,e.x,0,-f.y,f.x,0,-g.y,g.x,0];if(!a(h))return!1;h=[1,0,0,0,1,0,0, +0,1];if(!a(h))return!1;l.crossVectors(e,f);h=[l.x,l.y,l.z];return a(h)}}(),clampPoint:function(a,b){void 0===b&&(console.warn("THREE.Box3: .clampPoint() target is now required"),b=new p);return b.copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new p;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),getBoundingSphere:function(){var a=new p;return function(b){void 0===b&&(console.warn("THREE.Box3: .getBoundingSphere() target is now required"),b=new Fa); +this.getCenter(b.center);b.radius=.5*this.getSize(a).length();return b}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);this.isEmpty()&&this.makeEmpty();return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},applyMatrix4:function(){var a=[new p,new p,new p,new p,new p,new p,new p,new p];return function(b){if(this.isEmpty())return this;a[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(b);a[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(b); +a[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(b);a[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(b);a[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(b);a[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(b);a[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(b);a[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(b);this.setFromPoints(a);return this}}(),translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&& +a.max.equals(this.max)}});Object.assign(Fa.prototype,{set:function(a,b){this.center.copy(a);this.radius=b;return this},setFromPoints:function(){var a=new Wa;return function(b,c){var d=this.center;void 0!==c?d.copy(c):a.setFromPoints(b).getCenter(d);for(var e=c=0,f=b.length;e= +this.radius},containsPoint:function(a){return a.distanceToSquared(this.center)<=this.radius*this.radius},distanceToPoint:function(a){return a.distanceTo(this.center)-this.radius},intersectsSphere:function(a){var b=this.radius+a.radius;return a.center.distanceToSquared(this.center)<=b*b},intersectsBox:function(a){return a.intersectsSphere(this)},intersectsPlane:function(a){return Math.abs(a.distanceToPoint(this.center))<=this.radius},clampPoint:function(a,b){var c=this.center.distanceToSquared(a); +void 0===b&&(console.warn("THREE.Sphere: .clampPoint() target is now required"),b=new p);b.copy(a);c>this.radius*this.radius&&(b.sub(this.center).normalize(),b.multiplyScalar(this.radius).add(this.center));return b},getBoundingBox:function(a){void 0===a&&(console.warn("THREE.Sphere: .getBoundingBox() target is now required"),a=new Wa);a.set(this.center,this.center);a.expandByScalar(this.radius);return a},applyMatrix4:function(a){this.center.applyMatrix4(a);this.radius*=a.getMaxScaleOnAxis();return this}, +translate:function(a){this.center.add(a);return this},equals:function(a){return a.center.equals(this.center)&&a.radius===this.radius}});Object.assign(Pa.prototype,{set:function(a,b){this.normal.copy(a);this.constant=b;return this},setComponents:function(a,b,c,d){this.normal.set(a,b,c);this.constant=d;return this},setFromNormalAndCoplanarPoint:function(a,b){this.normal.copy(a);this.constant=-b.dot(this.normal);return this},setFromCoplanarPoints:function(){var a=new p,b=new p;return function(c,d,e){d= +a.subVectors(e,d).cross(b.subVectors(c,d)).normalize();this.setFromNormalAndCoplanarPoint(d,c);return this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(a){this.normal.copy(a.normal);this.constant=a.constant;return this},normalize:function(){var a=1/this.normal.length();this.normal.multiplyScalar(a);this.constant*=a;return this},negate:function(){this.constant*=-1;this.normal.negate();return this},distanceToPoint:function(a){return this.normal.dot(a)+this.constant},distanceToSphere:function(a){return this.distanceToPoint(a.center)- +a.radius},projectPoint:function(a,b){void 0===b&&(console.warn("THREE.Plane: .projectPoint() target is now required"),b=new p);return b.copy(this.normal).multiplyScalar(-this.distanceToPoint(a)).add(a)},intersectLine:function(){var a=new p;return function(b,c){void 0===c&&(console.warn("THREE.Plane: .intersectLine() target is now required"),c=new p);var d=b.delta(a),e=this.normal.dot(d);if(0===e){if(0===this.distanceToPoint(b.start))return c.copy(b.start)}else if(e=-(b.start.dot(this.normal)+this.constant)/ +e,!(0>e||1b&&0a&&0c;c++)b[c].copy(a.planes[c]);return this},setFromMatrix:function(a){var b=this.planes,c=a.elements;a=c[0];var d=c[1],e=c[2],f=c[3],g=c[4],h=c[5],k=c[6],m=c[7],l=c[8],n=c[9],q=c[10],p=c[11],t=c[12],u=c[13],y=c[14];c=c[15];b[0].setComponents(f-a,m-g,p-l,c-t).normalize();b[1].setComponents(f+a,m+g,p+l,c+t).normalize();b[2].setComponents(f+d,m+h,p+n,c+u).normalize();b[3].setComponents(f-d,m-h,p-n,c- +u).normalize();b[4].setComponents(f-e,m-k,p-q,c-y).normalize();b[5].setComponents(f+e,m+k,p+q,c+y).normalize();return this},intersectsObject:function(){var a=new Fa;return function(b){var c=b.geometry;null===c.boundingSphere&&c.computeBoundingSphere();a.copy(c.boundingSphere).applyMatrix4(b.matrixWorld);return this.intersectsSphere(a)}}(),intersectsSprite:function(){var a=new Fa;return function(b){a.center.set(0,0,0);a.radius=.7071067811865476;a.applyMatrix4(b.matrixWorld);return this.intersectsSphere(a)}}(), +intersectsSphere:function(a){var b=this.planes,c=a.center;a=-a.radius;for(var d=0;6>d;d++)if(b[d].distanceToPoint(c)d;d++){var e=c[d];a.x=0e.distanceToPoint(a))return!1}return!0}}(),containsPoint:function(a){for(var b=this.planes,c=0;6>c;c++)if(0>b[c].distanceToPoint(a))return!1;return!0}});var K= +{alphamap_fragment:"#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif\n",alphamap_pars_fragment:"#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif\n",alphatest_fragment:"#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif\n",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", +aomap_pars_fragment:"#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif",begin_vertex:"\nvec3 transformed = vec3( position );\n",beginnormal_vertex:"\nvec3 objectNormal = vec3( normal );\n",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", +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", +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", +clipping_planes_pars_fragment:"#if NUM_CLIPPING_PLANES > 0\n\t#if ! defined( PHYSICAL ) && ! defined( PHONG ) && ! defined( MATCAP )\n\t\tvarying vec3 vViewPosition;\n\t#endif\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif\n",clipping_planes_pars_vertex:"#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG ) && ! defined( MATCAP )\n\tvarying vec3 vViewPosition;\n#endif\n",clipping_planes_vertex:"#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG ) && ! defined( MATCAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n", +color_fragment:"#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif",color_pars_fragment:"#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif\n",color_pars_vertex:"#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif",color_vertex:"#ifdef USE_COLOR\n\tvColor.xyz = color.xyz;\n#endif",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", +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( sampler2D envMap, 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", +defaultnormal_vertex:"vec3 transformedNormal = normalMatrix * objectNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n",displacementmap_pars_vertex:"#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif\n",displacementmap_vertex:"#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif\n", +emissivemap_fragment:"#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif\n",emissivemap_pars_fragment:"#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif\n",encodings_fragment:" gl_FragColor = linearToOutputTexel( gl_FragColor );\n",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.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\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.a );\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.a );\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.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, 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.r, 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", +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", +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", +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",envmap_physical_pars_fragment:"#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( envMap, 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( envMap, 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", +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", +fog_vertex:"#ifdef USE_FOG\n\tfogDepth = -mvPosition.z;\n#endif\n",fog_pars_vertex:"#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif\n",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",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", +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",lightmap_fragment:"#ifdef USE_LIGHTMAP\n\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif\n", +lightmap_pars_fragment:"#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif",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", +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", +lights_phong_fragment:"BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;\n",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", +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", +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", +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", +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", +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",logdepthbuf_fragment:"#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif",logdepthbuf_pars_fragment:"#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n#endif\n", +logdepthbuf_pars_vertex:"#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif\n",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",map_fragment:"#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif\n", +map_pars_fragment:"#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n",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",map_particle_pars_fragment:"#ifdef USE_MAP\n\tuniform mat3 uvTransform;\n\tuniform sampler2D map;\n#endif\n",metalnessmap_fragment:"float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif\n", +metalnessmap_pars_fragment:"#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif",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",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", +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", +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",normal_fragment_maps:"#ifdef USE_NORMALMAP\n\t#ifdef OBJECTSPACE_NORMALMAP\n\t\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\t#ifdef FLIP_SIDED\n\t\t\tnormal = - normal;\n\t\t#endif\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t#endif\n\t\tnormal = normalize( normalMatrix * normal );\n\t#else\n\t\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n", +normalmap_pars_fragment:"#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\t#ifdef OBJECTSPACE_NORMALMAP\n\t\tuniform mat3 normalMatrix;\n\t#else\n\t\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\t\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\t\tvec2 st0 = dFdx( vUv.st );\n\t\t\tvec2 st1 = dFdy( vUv.st );\n\t\t\tfloat scale = sign( st1.t * st0.s - st0.t * st1.s );\n\t\t\tvec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );\n\t\t\tvec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );\n\t\t\tvec3 N = normalize( surf_norm );\n\t\t\tmat3 tsn = mat3( S, T, N );\n\t\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\t\tmapN.xy *= normalScale;\n\t\t\tmapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t\treturn normalize( tsn * mapN );\n\t\t}\n\t#endif\n#endif\n", +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", +premultiplied_alpha_fragment:"#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif\n",project_vertex:"vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\ngl_Position = projectionMatrix * mvPosition;\n",dithering_fragment:"#if defined( DITHERING )\n gl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif\n",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", +roughnessmap_fragment:"float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif\n",roughnessmap_pars_fragment:"#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif",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", +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", +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", +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", +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",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", +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",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", +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",specularmap_pars_fragment:"#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif",tonemapping_fragment:"#if defined( TONE_MAPPING )\n gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif\n",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", +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",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", +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",uv2_pars_fragment:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif",uv2_pars_vertex:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n#endif", +uv2_vertex:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = uv2;\n#endif",worldpos_vertex:"#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n#endif\n",background_frag:"uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tgl_FragColor = texture2D( t2D, vUv );\n}\n",background_vert:"varying vec2 vUv;\nvoid main() {\n\tvUv = uv;\n\tgl_Position = vec4( position, 1.0 );\n\tgl_Position.z = 1.0;\n}\n", +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",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",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", +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", +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", +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", +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",equirect_vert:"varying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}\n", +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", +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", +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", +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", +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 \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", +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 \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", +meshmatcap_frag:"#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\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 \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\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\tvec4 matcapColor = texture2D( matcap, uv );\n\tmatcapColor = matcapTexelToLinear( matcapColor );\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n", +meshmatcap_vert:"#define MATCAP\nvarying vec3 vViewPosition;\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 \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifndef FLAT_SHADED\n\t\tvNormal = normalize( transformedNormal );\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\tvViewPosition = - mvPosition.xyz;\n}\n", +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 \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 \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + 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", +meshphong_vert:"#define PHONG\nvarying vec3 vViewPosition;\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 \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#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}\n", +meshphysical_frag:"#define PHYSICAL\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifndef STANDARD\n\tuniform float clearCoat;\n\tuniform float clearCoatRoughness;\n#endif\nvarying vec3 vViewPosition;\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 \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 \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n", +meshphysical_vert:"#define PHYSICAL\nvarying vec3 vViewPosition;\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 \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#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}\n", +normal_frag:"#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}\n", +normal_vert:"#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\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#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}\n", +points_frag:"uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \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", +points_vert:"uniform float size;\nuniform float scale;\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\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}\n", +shadow_frag:"uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n}\n",shadow_vert:"#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n", +sprite_frag:"uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \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}\n", +sprite_vert:"uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}\n"}, +Ba={merge:function(a){for(var b={},c=0;c>16&255)/255;this.g=(a>>8&255)/255;this.b=(a&255)/255;return this},setRGB:function(a,b,c){this.r=a;this.g=b;this.b=c;return this},setHSL:function(){function a(a,c,d){0>d&&(d+=1);1d?c:d<2/3?a+6*(c-a)*(2/3-d):a}return function(b, +c,d){b=S.euclideanModulo(b,1);c=S.clamp(c,0,1);d=S.clamp(d,0,1);0===c?this.r=this.g=this.b=d:(c=.5>=d?d*(1+c):d+c-d*c,d=2*d-c,this.r=a(d,c,b+1/3),this.g=a(d,c,b),this.b=a(d,c,b-1/3));return this}}(),setStyle:function(a){function b(b){void 0!==b&&1>parseFloat(b)&&console.warn("THREE.Color: Alpha component of "+a+" will be ignored.")}var c;if(c=/^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec(a)){var d=c[2];switch(c[1]){case "rgb":case "rgba":if(c=/^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(d))return this.r= +Math.min(255,parseInt(c[1],10))/255,this.g=Math.min(255,parseInt(c[2],10))/255,this.b=Math.min(255,parseInt(c[3],10))/255,b(c[5]),this;if(c=/^(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(d))return this.r=Math.min(100,parseInt(c[1],10))/100,this.g=Math.min(100,parseInt(c[2],10))/100,this.b=Math.min(100,parseInt(c[3],10))/100,b(c[5]),this;break;case "hsl":case "hsla":if(c=/^([0-9]*\.?[0-9]+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(d)){d=parseFloat(c[1])/ +360;var e=parseInt(c[2],10)/100,f=parseInt(c[3],10)/100;b(c[5]);return this.setHSL(d,e,f)}}}else if(c=/^#([A-Fa-f0-9]+)$/.exec(a)){c=c[1];d=c.length;if(3===d)return this.r=parseInt(c.charAt(0)+c.charAt(0),16)/255,this.g=parseInt(c.charAt(1)+c.charAt(1),16)/255,this.b=parseInt(c.charAt(2)+c.charAt(2),16)/255,this;if(6===d)return this.r=parseInt(c.charAt(0)+c.charAt(1),16)/255,this.g=parseInt(c.charAt(2)+c.charAt(3),16)/255,this.b=parseInt(c.charAt(4)+c.charAt(5),16)/255,this}a&&0a?.0773993808*a:Math.pow(.9478672986*a+.0521327014,2.4)}return function(b){this.r=a(b.r);this.g=a(b.g);this.b=a(b.b);return this}}(),copyLinearToSRGB:function(){function a(a){return.0031308>a?12.92*a:1.055*Math.pow(a,.41666)-.055}return function(b){this.r=a(b.r);this.g=a(b.g);this.b=a(b.b);return this}}(),convertSRGBToLinear:function(){this.copySRGBToLinear(this); +return this},convertLinearToSRGB:function(){this.copyLinearToSRGB(this);return this},getHex:function(){return 255*this.r<<16^255*this.g<<8^255*this.b<<0},getHexString:function(){return("000000"+this.getHex().toString(16)).slice(-6)},getHSL:function(a){void 0===a&&(console.warn("THREE.Color: .getHSL() target is now required"),a={h:0,s:0,l:0});var b=this.r,c=this.g,d=this.b,e=Math.max(b,c,d),f=Math.min(b,c,d),g,h=(f+e)/2;if(f===e)f=g=0;else{var k=e-f;f=.5>=h?k/(e+f):k/(2-e-f);switch(e){case b:g=(c- +d)/k+(cMath.abs(g)?(this._x=Math.atan2(-m,e),this._z=Math.atan2(-f,a)):(this._x=Math.atan2(n,k),this._z= +0)):"YXZ"===b?(this._x=Math.asin(-d(m,-1,1)),.99999>Math.abs(m)?(this._y=Math.atan2(g,e),this._z=Math.atan2(h,k)):(this._y=Math.atan2(-l,a),this._z=0)):"ZXY"===b?(this._x=Math.asin(d(n,-1,1)),.99999>Math.abs(n)?(this._y=Math.atan2(-l,e),this._z=Math.atan2(-f,k)):(this._y=0,this._z=Math.atan2(h,a))):"ZYX"===b?(this._y=Math.asin(-d(l,-1,1)),.99999>Math.abs(l)?(this._x=Math.atan2(n,e),this._z=Math.atan2(h,a)):(this._x=0,this._z=Math.atan2(-f,k))):"YZX"===b?(this._z=Math.asin(d(h,-1,1)),.99999>Math.abs(h)? +(this._x=Math.atan2(-m,k),this._y=Math.atan2(-l,a)):(this._x=0,this._y=Math.atan2(g,e))):"XZY"===b?(this._z=Math.asin(-d(f,-1,1)),.99999>Math.abs(f)?(this._x=Math.atan2(n,k),this._y=Math.atan2(g,a)):(this._x=Math.atan2(-m,e),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+b);this._order=b;if(!1!==c)this.onChangeCallback();return this},setFromQuaternion:function(){var a=new P;return function(b,c,d){a.makeRotationFromQuaternion(b);return this.setFromRotationMatrix(a, +c,d)}}(),setFromVector3:function(a,b){return this.set(a.x,a.y,a.z,b||this._order)},reorder:function(){var a=new ja;return function(b){a.setFromEuler(this);return this.setFromQuaternion(a,b)}}(),equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._order===this._order},fromArray:function(a){this._x=a[0];this._y=a[1];this._z=a[2];void 0!==a[3]&&(this._order=a[3]);this.onChangeCallback();return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this._x;a[b+1]= +this._y;a[b+2]=this._z;a[b+3]=this._order;return a},toVector3:function(a){return a?a.set(this._x,this._y,this._z):new p(this._x,this._y,this._z)},onChange:function(a){this.onChangeCallback=a;return this},onChangeCallback:function(){}});Object.assign(Wd.prototype,{set:function(a){this.mask=1<g;g++)if(d[g]===d[(g+1)%3]){a.push(f);break}for(f=a.length-1;0<=f;f--)for(d=a[f],this.faces.splice(d,1),c=0,e=this.faceVertexUvs.length;cthis.opacity&&(d.opacity=this.opacity);!0===this.transparent&&(d.transparent=this.transparent);d.depthFunc=this.depthFunc;d.depthTest=this.depthTest;d.depthWrite=this.depthWrite;0!==this.rotation&&(d.rotation=this.rotation);!0===this.polygonOffset&&(d.polygonOffset=!0);0!==this.polygonOffsetFactor&&(d.polygonOffsetFactor=this.polygonOffsetFactor);0!==this.polygonOffsetUnits&&(d.polygonOffsetUnits=this.polygonOffsetUnits);1!==this.linewidth&&(d.linewidth=this.linewidth);void 0!== +this.dashSize&&(d.dashSize=this.dashSize);void 0!==this.gapSize&&(d.gapSize=this.gapSize);void 0!==this.scale&&(d.scale=this.scale);!0===this.dithering&&(d.dithering=!0);0a?b.copy(this.origin):b.copy(this.direction).multiplyScalar(a).add(this.origin)},distanceToPoint:function(a){return Math.sqrt(this.distanceSqToPoint(a))}, +distanceSqToPoint:function(){var a=new p;return function(b){var c=a.subVectors(b,this.origin).dot(this.direction);if(0>c)return this.origin.distanceToSquared(b);a.copy(this.direction).multiplyScalar(c).add(this.origin);return a.distanceToSquared(b)}}(),distanceSqToSegment:function(){var a=new p,b=new p,c=new p;return function(d,e,f,g){a.copy(d).add(e).multiplyScalar(.5);b.copy(e).sub(d).normalize();c.copy(this.origin).sub(a);var h=.5*d.distanceTo(e),k=-this.direction.dot(b),m=c.dot(this.direction), +l=-c.dot(b),n=c.lengthSq(),q=Math.abs(1-k*k);if(0=-p?e<=p?(h=1/q,d*=h,e*=h,k=d*(d+k*e+2*m)+e*(k*d+e+2*l)+n):(e=h,d=Math.max(0,-(k*e+m)),k=-d*d+e*(e+2*l)+n):(e=-h,d=Math.max(0,-(k*e+m)),k=-d*d+e*(e+2*l)+n):e<=-p?(d=Math.max(0,-(-k*h+m)),e=0b)return null;b=Math.sqrt(b-e);e=d-b;d+=b;return 0>e&&0>d?null:0>e?this.at(d,c):this.at(e,c)}}(),intersectsSphere:function(a){return this.distanceSqToPoint(a.center)<=a.radius*a.radius},distanceToPlane:function(a){var b= +a.normal.dot(this.direction);if(0===b)return 0===a.distanceToPoint(this.origin)?0:null;a=-(this.origin.dot(a.normal)+a.constant)/b;return 0<=a?a:null},intersectPlane:function(a,b){a=this.distanceToPlane(a);return null===a?null:this.at(a,b)},intersectsPlane:function(a){var b=a.distanceToPoint(this.origin);return 0===b||0>a.normal.dot(this.direction)*b?!0:!1},intersectBox:function(a,b){var c=1/this.direction.x;var d=1/this.direction.y;var e=1/this.direction.z,f=this.origin;if(0<=c){var g=(a.min.x-f.x)* +c;c*=a.max.x-f.x}else g=(a.max.x-f.x)*c,c*=a.min.x-f.x;if(0<=d){var h=(a.min.y-f.y)*d;d*=a.max.y-f.y}else h=(a.max.y-f.y)*d,d*=a.min.y-f.y;if(g>d||h>c)return null;if(h>g||g!==g)g=h;if(da||h>c)return null;if(h>g||g!==g)g=h;if(ac?null:this.at(0<=g?g:c,b)},intersectsBox:function(){var a=new p;return function(b){return null!==this.intersectBox(b,a)}}(),intersectTriangle:function(){var a= +new p,b=new p,c=new p,d=new p;return function(e,f,g,h,k){b.subVectors(f,e);c.subVectors(g,e);d.crossVectors(b,c);f=this.direction.dot(d);if(0f)h=-1,f=-f;else return null;a.subVectors(this.origin,e);e=h*this.direction.dot(c.crossVectors(a,c));if(0>e)return null;g=h*this.direction.dot(b.cross(a));if(0>g||e+g>f)return null;e=-h*a.dot(d);return 0>e?null:this.at(e/f,k)}}(),applyMatrix4:function(a){this.origin.applyMatrix4(a);this.direction.transformDirection(a);return this}, +equals:function(a){return a.origin.equals(this.origin)&&a.direction.equals(this.direction)}});Object.assign(da,{getNormal:function(){var a=new p;return function(b,c,d,e){void 0===e&&(console.warn("THREE.Triangle: .getNormal() target is now required"),e=new p);e.subVectors(d,c);a.subVectors(b,c);e.cross(a);b=e.lengthSq();return 0=a.x+a.y}}(),getUV:function(){var a=new p;return function(b,c,d,e,f,g,h,k){this.getBarycoord(b,c,d,e,a);k.set(0,0);k.addScaledVector(f, +a.x);k.addScaledVector(g,a.y);k.addScaledVector(h,a.z);return k}}()});Object.assign(da.prototype,{set:function(a,b,c){this.a.copy(a);this.b.copy(b);this.c.copy(c);return this},setFromPointsAndIndices:function(a,b,c,d){this.a.copy(a[b]);this.b.copy(a[c]);this.c.copy(a[d]);return this},clone:function(){return(new this.constructor).copy(this)},copy:function(a){this.a.copy(a.a);this.b.copy(a.b);this.c.copy(a.c);return this},getArea:function(){var a=new p,b=new p;return function(){a.subVectors(this.c, +this.b);b.subVectors(this.a,this.b);return.5*a.cross(b).length()}}(),getMidpoint:function(a){void 0===a&&(console.warn("THREE.Triangle: .getMidpoint() target is now required"),a=new p);return a.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},getNormal:function(a){return da.getNormal(this.a,this.b,this.c,a)},getPlane:function(a){void 0===a&&(console.warn("THREE.Triangle: .getPlane() target is now required"),a=new p);return a.setFromCoplanarPoints(this.a,this.b,this.c)},getBarycoord:function(a, +b){return da.getBarycoord(a,this.a,this.b,this.c,b)},containsPoint:function(a){return da.containsPoint(a,this.a,this.b,this.c)},getUV:function(a,b,c,d,e){return da.getUV(a,this.a,this.b,this.c,b,c,d,e)},intersectsBox:function(a){return a.intersectsTriangle(this)},closestPointToPoint:function(){var a=new p,b=new p,c=new p,d=new p,e=new p,f=new p;return function(g,h){void 0===h&&(console.warn("THREE.Triangle: .closestPointToPoint() target is now required"),h=new p);var k=this.a,m=this.b,l=this.c;a.subVectors(m, +k);b.subVectors(l,k);d.subVectors(g,k);var n=a.dot(d),q=b.dot(d);if(0>=n&&0>=q)return h.copy(k);e.subVectors(g,m);var v=a.dot(e),t=b.dot(e);if(0<=v&&t<=v)return h.copy(m);var u=n*t-v*q;if(0>=u&&0<=n&&0>=v)return m=n/(n-v),h.copy(k).addScaledVector(a,m);f.subVectors(g,l);g=a.dot(f);var y=b.dot(f);if(0<=y&&g<=y)return h.copy(l);n=g*q-n*y;if(0>=n&&0<=q&&0>=y)return u=q/(q-y),h.copy(k).addScaledVector(b,u);q=v*y-g*t;if(0>=q&&0<=t-v&&0<=g-y)return c.subVectors(l,m),u=(t-v)/(t-v+(g-y)),h.copy(m).addScaledVector(c, +u);l=1/(q+n+u);m=n*l;u*=l;return h.copy(k).addScaledVector(a,m).addScaledVector(b,u)}}(),equals:function(a){return a.a.equals(this.a)&&a.b.equals(this.b)&&a.c.equals(this.c)}});va.prototype=Object.create(L.prototype);va.prototype.constructor=va;va.prototype.isMeshBasicMaterial=!0;va.prototype.copy=function(a){L.prototype.copy.call(this,a);this.color.copy(a.color);this.map=a.map;this.lightMap=a.lightMap;this.lightMapIntensity=a.lightMapIntensity;this.aoMap=a.aoMap;this.aoMapIntensity=a.aoMapIntensity; +this.specularMap=a.specularMap;this.alphaMap=a.alphaMap;this.envMap=a.envMap;this.combine=a.combine;this.reflectivity=a.reflectivity;this.refractionRatio=a.refractionRatio;this.wireframe=a.wireframe;this.wireframeLinewidth=a.wireframeLinewidth;this.wireframeLinecap=a.wireframeLinecap;this.wireframeLinejoin=a.wireframeLinejoin;this.skinning=a.skinning;this.morphTargets=a.morphTargets;return this};ua.prototype=Object.assign(Object.create(C.prototype),{constructor:ua,isMesh:!0,setDrawMode:function(a){this.drawMode= +a},copy:function(a){C.prototype.copy.call(this,a);this.drawMode=a.drawMode;void 0!==a.morphTargetInfluences&&(this.morphTargetInfluences=a.morphTargetInfluences.slice());void 0!==a.morphTargetDictionary&&(this.morphTargetDictionary=Object.assign({},a.morphTargetDictionary));return this},updateMorphTargets:function(){var a=this.geometry;if(a.isBufferGeometry){a=a.morphAttributes;var b=Object.keys(a);if(0c.far?null:{distance:b,point:u.clone(),object:a}}function b(b,c,d,e,k,m,l,r,p){f.fromBufferAttribute(k,l);g.fromBufferAttribute(k,r);h.fromBufferAttribute(k,p);if(b=a(b,c,d,e,f,g,h,t))m&&(n.fromBufferAttribute(m,l),q.fromBufferAttribute(m,r),v.fromBufferAttribute(m,p),b.uv=da.getUV(t,f,g,h,n,q,v,new A)),m=new Xa(l,r,p),da.getNormal(f,g,h,m.normal),b.face=m;return b}var c=new P,d=new pb,e=new Fa,f=new p,g=new p, +h=new p,k=new p,m=new p,l=new p,n=new A,q=new A,v=new A,t=new p,u=new p;return function(r,p){var u=this.geometry,y=this.material,x=this.matrixWorld;if(void 0!==y&&(null===u.boundingSphere&&u.computeBoundingSphere(),e.copy(u.boundingSphere),e.applyMatrix4(x),!1!==r.ray.intersectsSphere(e)&&(c.getInverse(x),d.copy(r.ray).applyMatrix4(c),null===u.boundingBox||!1!==d.intersectsBox(u.boundingBox))))if(u.isBufferGeometry){var z=u.index,C=u.attributes.position,B=u.attributes.uv,F=u.groups;u=u.drawRange; +var I;if(null!==z)if(Array.isArray(y)){var G=0;for(I=F.length;Ge.far||f.push({distance:t,point:b.clone(),uv:da.getUV(b, +h,k,m,l,n,q,new A),face:null,object:this})}}(),clone:function(){return(new this.constructor(this.material)).copy(this)},copy:function(a){C.prototype.copy.call(this,a);void 0!==a.center&&this.center.copy(a.center);return this}});Fc.prototype=Object.assign(Object.create(C.prototype),{constructor:Fc,copy:function(a){C.prototype.copy.call(this,a,!1);a=a.levels;for(var b=0,c=a.length;b=d[e].distance)d[e-1].object.visible=!1,d[e].object.visible=!0;else break;for(;ef||(l.applyMatrix4(this.matrixWorld),u=d.ray.origin.distanceTo(l),ud.far||e.push({distance:u,point:h.clone().applyMatrix4(this.matrixWorld),index:g,face:null,faceIndex:null,object:this}))}}else for(g=0,t=v.length/3-1;gf||(l.applyMatrix4(this.matrixWorld),u=d.ray.origin.distanceTo(l),ud.far||e.push({distance:u,point:h.clone().applyMatrix4(this.matrixWorld),index:g,face:null,faceIndex:null,object:this}))}else if(g.isGeometry)for(k=g.vertices,m=k.length,g=0;gf||(l.applyMatrix4(this.matrixWorld),u=d.ray.origin.distanceTo(l),ud.far||e.push({distance:u,point:h.clone().applyMatrix4(this.matrixWorld), +index:g,face:null,faceIndex:null,object:this}))}}}(),clone:function(){return(new this.constructor(this.geometry,this.material)).copy(this)}});W.prototype=Object.assign(Object.create(pa.prototype),{constructor:W,isLineSegments:!0,computeLineDistances:function(){var a=new p,b=new p;return function(){var c=this.geometry;if(c.isBufferGeometry)if(null===c.index){for(var d=c.attributes.position,e=[],f=0,g=d.count;fd.far||e.push({distance:a,distanceToRay:Math.sqrt(f),point:n.clone(),index:c,face:null,object:g}))}var g=this,h=this.geometry,k=this.matrixWorld,m=d.params.Points.threshold;null===h.boundingSphere&&h.computeBoundingSphere();c.copy(h.boundingSphere);c.applyMatrix4(k);c.radius+=m;if(!1!==d.ray.intersectsSphere(c)){a.getInverse(k);b.copy(d.ray).applyMatrix4(a);m/=(this.scale.x+this.scale.y+this.scale.z)/3;var l=m*m;m=new p;var n=new p;if(h.isBufferGeometry){var q=h.index;h=h.attributes.position.array; +if(null!==q){var v=q.array;q=0;for(var t=v.length;q=a.HAVE_CURRENT_DATA&&(this.needsUpdate=!0)}});Tb.prototype= +Object.create(Q.prototype);Tb.prototype.constructor=Tb;Tb.prototype.isCompressedTexture=!0;Hc.prototype=Object.create(Q.prototype);Hc.prototype.constructor=Hc;Hc.prototype.isCanvasTexture=!0;Ic.prototype=Object.create(Q.prototype);Ic.prototype.constructor=Ic;Ic.prototype.isDepthTexture=!0;Ub.prototype=Object.create(F.prototype);Ub.prototype.constructor=Ub;Jc.prototype=Object.create(M.prototype);Jc.prototype.constructor=Jc;Vb.prototype=Object.create(F.prototype);Vb.prototype.constructor=Vb;Kc.prototype= +Object.create(M.prototype);Kc.prototype.constructor=Kc;ma.prototype=Object.create(F.prototype);ma.prototype.constructor=ma;Lc.prototype=Object.create(M.prototype);Lc.prototype.constructor=Lc;Wb.prototype=Object.create(ma.prototype);Wb.prototype.constructor=Wb;Mc.prototype=Object.create(M.prototype);Mc.prototype.constructor=Mc;rb.prototype=Object.create(ma.prototype);rb.prototype.constructor=rb;Nc.prototype=Object.create(M.prototype);Nc.prototype.constructor=Nc;Xb.prototype=Object.create(ma.prototype); +Xb.prototype.constructor=Xb;Oc.prototype=Object.create(M.prototype);Oc.prototype.constructor=Oc;Yb.prototype=Object.create(ma.prototype);Yb.prototype.constructor=Yb;Pc.prototype=Object.create(M.prototype);Pc.prototype.constructor=Pc;Zb.prototype=Object.create(F.prototype);Zb.prototype.constructor=Zb;Qc.prototype=Object.create(M.prototype);Qc.prototype.constructor=Qc;$b.prototype=Object.create(F.prototype);$b.prototype.constructor=$b;Rc.prototype=Object.create(M.prototype);Rc.prototype.constructor= +Rc;ac.prototype=Object.create(F.prototype);ac.prototype.constructor=ac;var Xg={triangulate:function(a,b,c){c=c||2;var d=b&&b.length,e=d?b[0]*c:a.length,f=df(a,0,e,c,!0),g=[];if(!f)return g;var h;if(d){var k=c;d=[];var m;var l=0;for(m=b.length;l80*c){var p=h=a[0];var t=d=a[1];for(k= +c;kh&&(h=l),b>d&&(d=b);h=Math.max(h-p,d-t);h=0!==h?1/h:0}Uc(f,g,c,p,t,h);return g}},Za={area:function(a){for(var b=a.length,c=0,d=b-1,e=0;eZa.area(a)},triangulateShape:function(a,b){var c=[],d=[],e=[];hf(a);jf(c,a);var f=a.length;b.forEach(hf);for(a=0;aMath.abs(g-k)?[new A(a,1-c),new A(h,1-d),new A(m,1-e),new A(n,1-b)]:[new A(g,1-c),new A(k,1-d),new A(l,1-e),new A(q,1-b)]}};Wc.prototype=Object.create(M.prototype);Wc.prototype.constructor=Wc;bc.prototype=Object.create(Sa.prototype);bc.prototype.constructor=bc;Xc.prototype= +Object.create(M.prototype);Xc.prototype.constructor=Xc;ub.prototype=Object.create(F.prototype);ub.prototype.constructor=ub;Yc.prototype=Object.create(M.prototype);Yc.prototype.constructor=Yc;cc.prototype=Object.create(F.prototype);cc.prototype.constructor=cc;Zc.prototype=Object.create(M.prototype);Zc.prototype.constructor=Zc;dc.prototype=Object.create(F.prototype);dc.prototype.constructor=dc;vb.prototype=Object.create(M.prototype);vb.prototype.constructor=vb;vb.prototype.toJSON=function(){var a=M.prototype.toJSON.call(this); +return lf(this.parameters.shapes,a)};wb.prototype=Object.create(F.prototype);wb.prototype.constructor=wb;wb.prototype.toJSON=function(){var a=F.prototype.toJSON.call(this);return lf(this.parameters.shapes,a)};ec.prototype=Object.create(F.prototype);ec.prototype.constructor=ec;xb.prototype=Object.create(M.prototype);xb.prototype.constructor=xb;$a.prototype=Object.create(F.prototype);$a.prototype.constructor=$a;$c.prototype=Object.create(xb.prototype);$c.prototype.constructor=$c;ad.prototype=Object.create($a.prototype); +ad.prototype.constructor=ad;bd.prototype=Object.create(M.prototype);bd.prototype.constructor=bd;fc.prototype=Object.create(F.prototype);fc.prototype.constructor=fc;var Aa=Object.freeze({WireframeGeometry:Ub,ParametricGeometry:Jc,ParametricBufferGeometry:Vb,TetrahedronGeometry:Lc,TetrahedronBufferGeometry:Wb,OctahedronGeometry:Mc,OctahedronBufferGeometry:rb,IcosahedronGeometry:Nc,IcosahedronBufferGeometry:Xb,DodecahedronGeometry:Oc,DodecahedronBufferGeometry:Yb,PolyhedronGeometry:Kc,PolyhedronBufferGeometry:ma, +TubeGeometry:Pc,TubeBufferGeometry:Zb,TorusKnotGeometry:Qc,TorusKnotBufferGeometry:$b,TorusGeometry:Rc,TorusBufferGeometry:ac,TextGeometry:Wc,TextBufferGeometry:bc,SphereGeometry:Xc,SphereBufferGeometry:ub,RingGeometry:Yc,RingBufferGeometry:cc,PlaneGeometry:yc,PlaneBufferGeometry:ob,LatheGeometry:Zc,LatheBufferGeometry:dc,ShapeGeometry:vb,ShapeBufferGeometry:wb,ExtrudeGeometry:tb,ExtrudeBufferGeometry:Sa,EdgesGeometry:ec,ConeGeometry:$c,ConeBufferGeometry:ad,CylinderGeometry:xb,CylinderBufferGeometry:$a, +CircleGeometry:bd,CircleBufferGeometry:fc,BoxGeometry:Kb,BoxBufferGeometry:nb});yb.prototype=Object.create(L.prototype);yb.prototype.constructor=yb;yb.prototype.isShadowMaterial=!0;yb.prototype.copy=function(a){L.prototype.copy.call(this,a);this.color.copy(a.color);return this};gc.prototype=Object.create(la.prototype);gc.prototype.constructor=gc;gc.prototype.isRawShaderMaterial=!0;Ta.prototype=Object.create(L.prototype);Ta.prototype.constructor=Ta;Ta.prototype.isMeshStandardMaterial=!0;Ta.prototype.copy= +function(a){L.prototype.copy.call(this,a);this.defines={STANDARD:""};this.color.copy(a.color);this.roughness=a.roughness;this.metalness=a.metalness;this.map=a.map;this.lightMap=a.lightMap;this.lightMapIntensity=a.lightMapIntensity;this.aoMap=a.aoMap;this.aoMapIntensity=a.aoMapIntensity;this.emissive.copy(a.emissive);this.emissiveMap=a.emissiveMap;this.emissiveIntensity=a.emissiveIntensity;this.bumpMap=a.bumpMap;this.bumpScale=a.bumpScale;this.normalMap=a.normalMap;this.normalMapType=a.normalMapType; +this.normalScale.copy(a.normalScale);this.displacementMap=a.displacementMap;this.displacementScale=a.displacementScale;this.displacementBias=a.displacementBias;this.roughnessMap=a.roughnessMap;this.metalnessMap=a.metalnessMap;this.alphaMap=a.alphaMap;this.envMap=a.envMap;this.envMapIntensity=a.envMapIntensity;this.refractionRatio=a.refractionRatio;this.wireframe=a.wireframe;this.wireframeLinewidth=a.wireframeLinewidth;this.wireframeLinecap=a.wireframeLinecap;this.wireframeLinejoin=a.wireframeLinejoin; +this.skinning=a.skinning;this.morphTargets=a.morphTargets;this.morphNormals=a.morphNormals;return this};zb.prototype=Object.create(Ta.prototype);zb.prototype.constructor=zb;zb.prototype.isMeshPhysicalMaterial=!0;zb.prototype.copy=function(a){Ta.prototype.copy.call(this,a);this.defines={PHYSICAL:""};this.reflectivity=a.reflectivity;this.clearCoat=a.clearCoat;this.clearCoatRoughness=a.clearCoatRoughness;return this};Ha.prototype=Object.create(L.prototype);Ha.prototype.constructor=Ha;Ha.prototype.isMeshPhongMaterial= +!0;Ha.prototype.copy=function(a){L.prototype.copy.call(this,a);this.color.copy(a.color);this.specular.copy(a.specular);this.shininess=a.shininess;this.map=a.map;this.lightMap=a.lightMap;this.lightMapIntensity=a.lightMapIntensity;this.aoMap=a.aoMap;this.aoMapIntensity=a.aoMapIntensity;this.emissive.copy(a.emissive);this.emissiveMap=a.emissiveMap;this.emissiveIntensity=a.emissiveIntensity;this.bumpMap=a.bumpMap;this.bumpScale=a.bumpScale;this.normalMap=a.normalMap;this.normalMapType=a.normalMapType; +this.normalScale.copy(a.normalScale);this.displacementMap=a.displacementMap;this.displacementScale=a.displacementScale;this.displacementBias=a.displacementBias;this.specularMap=a.specularMap;this.alphaMap=a.alphaMap;this.envMap=a.envMap;this.combine=a.combine;this.reflectivity=a.reflectivity;this.refractionRatio=a.refractionRatio;this.wireframe=a.wireframe;this.wireframeLinewidth=a.wireframeLinewidth;this.wireframeLinecap=a.wireframeLinecap;this.wireframeLinejoin=a.wireframeLinejoin;this.skinning= +a.skinning;this.morphTargets=a.morphTargets;this.morphNormals=a.morphNormals;return this};Ab.prototype=Object.create(Ha.prototype);Ab.prototype.constructor=Ab;Ab.prototype.isMeshToonMaterial=!0;Ab.prototype.copy=function(a){Ha.prototype.copy.call(this,a);this.gradientMap=a.gradientMap;return this};Bb.prototype=Object.create(L.prototype);Bb.prototype.constructor=Bb;Bb.prototype.isMeshNormalMaterial=!0;Bb.prototype.copy=function(a){L.prototype.copy.call(this,a);this.bumpMap=a.bumpMap;this.bumpScale= +a.bumpScale;this.normalMap=a.normalMap;this.normalMapType=a.normalMapType;this.normalScale.copy(a.normalScale);this.displacementMap=a.displacementMap;this.displacementScale=a.displacementScale;this.displacementBias=a.displacementBias;this.wireframe=a.wireframe;this.wireframeLinewidth=a.wireframeLinewidth;this.skinning=a.skinning;this.morphTargets=a.morphTargets;this.morphNormals=a.morphNormals;return this};Cb.prototype=Object.create(L.prototype);Cb.prototype.constructor=Cb;Cb.prototype.isMeshLambertMaterial= +!0;Cb.prototype.copy=function(a){L.prototype.copy.call(this,a);this.color.copy(a.color);this.map=a.map;this.lightMap=a.lightMap;this.lightMapIntensity=a.lightMapIntensity;this.aoMap=a.aoMap;this.aoMapIntensity=a.aoMapIntensity;this.emissive.copy(a.emissive);this.emissiveMap=a.emissiveMap;this.emissiveIntensity=a.emissiveIntensity;this.specularMap=a.specularMap;this.alphaMap=a.alphaMap;this.envMap=a.envMap;this.combine=a.combine;this.reflectivity=a.reflectivity;this.refractionRatio=a.refractionRatio; +this.wireframe=a.wireframe;this.wireframeLinewidth=a.wireframeLinewidth;this.wireframeLinecap=a.wireframeLinecap;this.wireframeLinejoin=a.wireframeLinejoin;this.skinning=a.skinning;this.morphTargets=a.morphTargets;this.morphNormals=a.morphNormals;return this};Db.prototype=Object.create(L.prototype);Db.prototype.constructor=Db;Db.prototype.isMeshMatcapMaterial=!0;Db.prototype.copy=function(a){L.prototype.copy.call(this,a);this.defines={MATCAP:""};this.color.copy(a.color);this.map=this.matcap=a.map; +this.bumpMap=a.bumpMap;this.bumpScale=a.bumpScale;this.normalMap=a.normalMap;this.normalMapType=a.normalMapType;this.normalScale.copy(a.normalScale);this.displacementMap=a.displacementMap;this.displacementScale=a.displacementScale;this.displacementBias=a.displacementBias;this.alphaMap=a.alphaMap;this.skinning=a.skinning;this.morphTargets=a.morphTargets;this.morphNormals=a.morphNormals;return this};Eb.prototype=Object.create(T.prototype);Eb.prototype.constructor=Eb;Eb.prototype.isLineDashedMaterial= +!0;Eb.prototype.copy=function(a){T.prototype.copy.call(this,a);this.scale=a.scale;this.dashSize=a.dashSize;this.gapSize=a.gapSize;return this};var Yg=Object.freeze({ShadowMaterial:yb,SpriteMaterial:fb,RawShaderMaterial:gc,ShaderMaterial:la,PointsMaterial:Ga,MeshPhysicalMaterial:zb,MeshStandardMaterial:Ta,MeshPhongMaterial:Ha,MeshToonMaterial:Ab,MeshNormalMaterial:Bb,MeshLambertMaterial:Cb,MeshDepthMaterial:cb,MeshDistanceMaterial:db,MeshBasicMaterial:va,MeshMatcapMaterial:Db,LineDashedMaterial:Eb, +LineBasicMaterial:T,Material:L}),Ib={enabled:!1,files:{},add:function(a,b){!1!==this.enabled&&(this.files[a]=b)},get:function(a){if(!1!==this.enabled)return this.files[a]},remove:function(a){delete this.files[a]},clear:function(){this.files={}}},xa=new ee,Oa={};Object.assign(Ia.prototype,{load:function(a,b,c,d){void 0===a&&(a="");void 0!==this.path&&(a=this.path+a);a=this.manager.resolveURL(a);var e=this,f=Ib.get(a);if(void 0!==f)return e.manager.itemStart(a),setTimeout(function(){b&&b(f);e.manager.itemEnd(a)}, +0),f;if(void 0!==Oa[a])Oa[a].push({onLoad:b,onProgress:c,onError:d});else{var g=a.match(/^data:(.*?)(;base64)?,(.*)$/);if(g){c=g[1];var h=!!g[2];g=g[3];g=window.decodeURIComponent(g);h&&(g=window.atob(g));try{var k=(this.responseType||"").toLowerCase();switch(k){case "arraybuffer":case "blob":var m=new Uint8Array(g.length);for(h=0;hg)e=a+1;else if(0b&&(b=0);1Number.EPSILON&&(g.normalize(), +c=Math.acos(S.clamp(d[k-1].dot(d[k]),-1,1)),e[k].applyMatrix4(h.makeRotationAxis(g,c))),f[k].crossVectors(d[k],e[k]);if(!0===b)for(c=Math.acos(S.clamp(e[0].dot(e[a]),-1,1)),c/=a,0d;)d+=c;for(;d>c;)d-=c;de&&(e=1);1E-4>d&&(d=e);1E-4>k&&(k=e);Ce.initNonuniformCatmullRom(f.x,g.x,h.x, +c.x,d,e,k);De.initNonuniformCatmullRom(f.y,g.y,h.y,c.y,d,e,k);Ee.initNonuniformCatmullRom(f.z,g.z,h.z,c.z,d,e,k)}else"catmullrom"===this.curveType&&(Ce.initCatmullRom(f.x,g.x,h.x,c.x,this.tension),De.initCatmullRom(f.y,g.y,h.y,c.y,this.tension),Ee.initCatmullRom(f.z,g.z,h.z,c.z,this.tension));b.set(Ce.calc(a),De.calc(a),Ee.calc(a));return b};ha.prototype.copy=function(a){N.prototype.copy.call(this,a);this.points=[];for(var b=0,c=a.points.length;bc.length- +2?c.length-1:a+1];c=c[a>c.length-3?c.length-1:a+2];b.set(nf(d,e.x,f.x,g.x,c.x),nf(d,e.y,f.y,g.y,c.y));return b};Ma.prototype.copy=function(a){N.prototype.copy.call(this,a);this.points=[];for(var b=0,c=a.points.length;b=b)return b=c[a]-b,a=this.curves[a],c=a.getLength(),a.getPointAt(0===c?0:1-b/c);a++}return null},getLength:function(){var a=this.getCurveLengths();return a[a.length-1]},updateArcLengths:function(){this.needsUpdate=!0;this.cacheLengths=null;this.getCurveLengths()},getCurveLengths:function(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths; +for(var a=[],b=0,c=0,d=this.curves.length;c=e)break a;else{f=b[1];a=e)break b}d=c;c=0}}for(;c>>1,ab;)--f;++f;if(0!==e||f!==d)e>=f&&(f=Math.max(f,1),e=f-1),a=this.getValueSize(),this.times=ra.arraySlice(c,e,f),this.values=ra.arraySlice(this.values,e*a,f*a);return this},validate:function(){var a=!0,b=this.getValueSize();0!==b-Math.floor(b)&&(console.error("THREE.KeyframeTrack: Invalid value size in track.",this),a=!1);var c=this.times; +b=this.values;var d=c.length;0===d&&(console.error("THREE.KeyframeTrack: Track is empty.",this),a=!1);for(var e=null,f=0;f!==d;f++){var g=c[f];if("number"===typeof g&&isNaN(g)){console.error("THREE.KeyframeTrack: Time is not a valid number.",this,f,g);a=!1;break}if(null!==e&&e>g){console.error("THREE.KeyframeTrack: Out of order keys.",this,f,g,e);a=!1;break}e=g}if(void 0!==b&&ra.isTypedArray(b))for(f=0,c=b.length;f!==c;++f)if(d=b[f],isNaN(d)){console.error("THREE.KeyframeTrack: Value is not a valid number.", +this,f,d);a=!1;break}return a},optimize:function(){for(var a=this.times,b=this.values,c=this.getValueSize(),d=2302===this.getInterpolation(),e=1,f=a.length-1,g=1;gm.opacity&&(m.transparent=!0);d.setTextures(k);return d.parse(m)}}()});var Fe= +{decodeText:function(a){if("undefined"!==typeof TextDecoder)return(new TextDecoder).decode(a);for(var b="",c=0,d=a.length;cf;f++){var z=h[v++];var C=D[2*z];z=D[2*z+1];C=new A(C,z);2!==f&&c.faceVertexUvs[e][u].push(C);0!==f&&c.faceVertexUvs[e][u+1].push(C)}}y&&(y=3*h[v++],t.normal.set(m[y++],m[y++],m[y]),w.normal.copy(t.normal));if(x)for(e=0;4>e;e++)y=3*h[v++],x=new p(m[y++],m[y++],m[y]),2!==e&&t.vertexNormals.push(x),0!==e&&w.vertexNormals.push(x);n&&(n=h[v++],n=l[n],t.color.setHex(n), +w.color.setHex(n));if(k)for(e=0;4>e;e++)n=h[v++],n=l[n],2!==e&&t.vertexColors.push(new G(n)),0!==e&&w.vertexColors.push(new G(n));c.faces.push(t);c.faces.push(w)}else{t=new Xa;t.a=h[v++];t.b=h[v++];t.c=h[v++];u&&(u=h[v++],t.materialIndex=u);u=c.faces.length;if(e)for(e=0;ef;f++)z=h[v++],C=D[2*z],z=D[2*z+1],C=new A(C,z),c.faceVertexUvs[e][u].push(C);y&&(y=3*h[v++],t.normal.set(m[y++],m[y++],m[y]));if(x)for(e=0;3>e;e++)y=3*h[v++],x=new p(m[y++],m[y++], +m[y]),t.vertexNormals.push(x);n&&(n=h[v++],t.color.setHex(l[n]));if(k)for(e=0;3>e;e++)n=h[v++],t.vertexColors.push(new G(l[n]));c.faces.push(t)}}d=a;v=void 0!==d.influencesPerVertex?d.influencesPerVertex:2;if(d.skinWeights)for(g=0,h=d.skinWeights.length;g +Number.EPSILON){if(0>m&&(g=b[f],k=-k,h=b[e],m=-m),!(a.yh.y))if(a.y===g.y){if(a.x===g.x)return!0}else{e=m*(a.x-g.x)-k*(a.y-g.y);if(0===e)return!0;0>e||(d=!d)}}else if(a.y===g.y&&(h.x<=a.x&&a.x<=g.x||g.x<=a.x&&a.x<=h.x))return!0}return d}var e=Za.isClockWise,f=this.subPaths;if(0===f.length)return[];if(!0===b)return c(f);b=[];if(1===f.length){var g=f[0];var h=new gb;h.curves=g.curves;b.push(h);return b}var k=!e(f[0].getPoints());k=a?!k:k;h=[];var m=[],l=[],n=0;m[n]=void 0;l[n]=[];for(var p= +0,v=f.length;pd&&this._mixBufferRegion(c,a,3*b,1-d,b);d=b;for(var f=b+b;d!==f;++d)if(c[d]!==c[d+b]){e.setValue(c,a);break}},saveOriginalState:function(){var a=this.buffer,b=this.valueSize,c=3*b;this.binding.getValue(a, +c);for(var d=b;d!==c;++d)a[d]=a[c+d%b];this.cumulativeWeight=0},restoreOriginalState:function(){this.binding.setValue(this.buffer,3*this.valueSize)},_select:function(a,b,c,d,e){if(.5<=d)for(d=0;d!==e;++d)a[b+d]=a[c+d]},_slerp:function(a,b,c,d){ja.slerpFlat(a,b,a,b,a,c,d)},_lerp:function(a,b,c,d,e){for(var f=1-d,g=0;g!==e;++g){var h=b+g;a[h]=a[h]*f+a[c+g]*d}}});Object.assign(rf.prototype,{getValue:function(a,b){this.bind();var c=this._bindings[this._targetGroup.nCachedObjects_];void 0!==c&&c.getValue(a, +b)},setValue:function(a,b){for(var c=this._bindings,d=this._targetGroup.nCachedObjects_,e=c.length;d!==e;++d)c[d].setValue(a,b)},bind:function(){for(var a=this._bindings,b=this._targetGroup.nCachedObjects_,c=a.length;b!==c;++b)a[b].bind()},unbind:function(){for(var a=this._bindings,b=this._targetGroup.nCachedObjects_,c=a.length;b!==c;++b)a[b].unbind()}});Object.assign(ta,{Composite:rf,create:function(a,b,c){return a&&a.isAnimationObjectGroup?new ta.Composite(a,b,c):new ta(a,b,c)},sanitizeNodeName:function(){var a= +/[\[\]\.:\/]/g;return function(b){return b.replace(/\s/g,"_").replace(a,"")}}(),parseTrackName:function(){var a="[^"+"\\[\\]\\.:\\/".replace("\\.","")+"]",b=/((?:WC+[\/:])*)/.source.replace("WC","[^\\[\\]\\.:\\/]");a=/(WCOD+)?/.source.replace("WCOD",a);var c=/(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace("WC","[^\\[\\]\\.:\\/]"),d=/\.(WC+)(?:\[(.+)\])?/.source.replace("WC","[^\\[\\]\\.:\\/]"),e=new RegExp("^"+b+a+c+d+"$"),f=["material","materials","bones"];return function(a){var b=e.exec(a);if(!b)throw Error("PropertyBinding: Cannot parse trackName: "+ +a);b={nodeName:b[2],objectName:b[3],objectIndex:b[4],propertyName:b[5],propertyIndex:b[6]};var c=b.nodeName&&b.nodeName.lastIndexOf(".");if(void 0!==c&&-1!==c){var d=b.nodeName.substring(c+1);-1!==f.indexOf(d)&&(b.nodeName=b.nodeName.substring(0,c),b.objectName=d)}if(null===b.propertyName||0===b.propertyName.length)throw Error("PropertyBinding: can not parse propertyName from trackName: "+a);return b}}(),findNode:function(a,b){if(!b||""===b||"root"===b||"."===b||-1===b||b===a.name||b===a.uuid)return a; +if(a.skeleton){var c=a.skeleton.getBoneByName(b);if(void 0!==c)return c}if(a.children){var d=function(a){for(var c=0;c=b){var p=b++,n=a[p];c[n.uuid]=l;a[l]=n;c[k]=p;a[p]=h;h=0;for(k=e;h!==k;++h){n=d[h];var q=n[l];n[l]=n[p];n[p]=q}}}this.nCachedObjects_=b},uncache:function(){for(var a=this._objects,b=a.length,c=this.nCachedObjects_,d=this._indicesByUUID,e=this._bindings,f=e.length,g=0,h=arguments.length;g!==h;++g){var k= +arguments[g].uuid,l=d[k];if(void 0!==l)if(delete d[k],lb||0===c)return;this._startTime=null;b*=c}b*=this._updateTimeScale(a);c=this._updateTime(b);a=this._updateWeight(a);if(0c.parameterPositions[1]&&(this.stopFading(),0===d&&(this.enabled=!1))}}return this._effectiveWeight=b},_updateTimeScale:function(a){var b=0;if(!this.paused){b=this.timeScale;var c=this._timeScaleInterpolant;if(null!==c){var d=c.evaluate(a)[0];b*=d;a>c.parameterPositions[1]&&(this.stopWarping(),0===b?this.paused=!0:this.timeScale=b)}}return this._effectiveTimeScale=b},_updateTime:function(a){var b=this.time+a,c=this._clip.duration,d=this.loop,e=this._loopCount,f=2202===d;if(0===a)return-1=== +e?b:f&&1===(e&1)?c-b:b;if(2200===d)a:{if(-1===e&&(this._loopCount=0,this._setEndings(!0,!0,!1)),b>=c)b=c;else if(0>b)b=0;else break a;this.clampWhenFinished?this.paused=!0:this.enabled=!1;this._mixer.dispatchEvent({type:"finished",action:this,direction:0>a?-1:1})}else{-1===e&&(0<=a?(e=0,this._setEndings(!0,0===this.repetitions,f)):this._setEndings(0===this.repetitions,!0,f));if(b>=c||0>b){d=Math.floor(b/c);b-=c*d;e+=Math.abs(d);var g=this.repetitions-e;0>=g?(this.clampWhenFinished?this.paused=!0: +this.enabled=!1,b=0a,this._setEndings(a,!a,f)):this._setEndings(!1,!1,f),this._loopCount=e,this._mixer.dispatchEvent({type:"loop",action:this,loopDelta:d}))}if(f&&1===(e&1))return this.time=b,c-b}return this.time=b},_setEndings:function(a,b,c){var d=this._interpolantSettings;c?(d.endingStart=2401,d.endingEnd=2401):(d.endingStart=a?this.zeroSlopeAtStart?2401:2400:2402,d.endingEnd=b?this.zeroSlopeAtEnd?2401: +2400:2402)},_scheduleFading:function(a,b,c){var d=this._mixer,e=d.time,f=this._weightInterpolant;null===f&&(this._weightInterpolant=f=d._lendControlInterpolant());d=f.parameterPositions;f=f.sampleValues;d[0]=e;f[0]=b;d[1]=e+a;f[1]=c;return this}});te.prototype=Object.assign(Object.create(ia.prototype),{constructor:te,_bindAction:function(a,b){var c=a._localRoot||this._root,d=a._clip.tracks,e=d.length,f=a._propertyBindings;a=a._interpolants;var g=c.uuid,h=this._bindingsByRootAndName,k=h[g];void 0=== +k&&(k={},h[g]=k);for(h=0;h!==e;++h){var l=d[h],p=l.name,n=k[p];if(void 0===n){n=f[h];if(void 0!==n){null===n._cacheIndex&&(++n.referenceCount,this._addInactiveBinding(n,g,p));continue}n=new se(ta.create(c,p,b&&b._propertyBindings[h].binding.parsedPath),l.ValueTypeName,l.getValueSize());++n.referenceCount;this._addInactiveBinding(n,g,p)}f[h]=n;a[h].resultBuffer=n.buffer}},_activateAction:function(a){if(!this._isActiveAction(a)){if(null===a._cacheIndex){var b=(a._localRoot||this._root).uuid,c=a._clip.uuid, +d=this._actionsByClip[c];this._bindAction(a,d&&d.knownActions[0]);this._addInactiveAction(a,c,b)}b=a._propertyBindings;c=0;for(d=b.length;c!==d;++c){var e=b[c];0===e.useCount++&&(this._lendBinding(e),e.saveOriginalState())}this._lendAction(a)}},_deactivateAction:function(a){if(this._isActiveAction(a)){for(var b=a._propertyBindings,c=0,d=b.length;c!==d;++c){var e=b[c];0===--e.useCount&&(e.restoreOriginalState(),this._takeBackBinding(e))}this._takeBackAction(a)}},_initMemoryManager:function(){this._actions= +[];this._nActiveActions=0;this._actionsByClip={};this._bindings=[];this._nActiveBindings=0;this._bindingsByRootAndName={};this._controlInterpolants=[];this._nActiveControlInterpolants=0;var a=this;this.stats={actions:{get total(){return a._actions.length},get inUse(){return a._nActiveActions}},bindings:{get total(){return a._bindings.length},get inUse(){return a._nActiveBindings}},controlInterpolants:{get total(){return a._controlInterpolants.length},get inUse(){return a._nActiveControlInterpolants}}}}, +_isActiveAction:function(a){a=a._cacheIndex;return null!==a&&athis.max.x||a.ythis.max.y?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y},getParameter:function(a,b){void 0===b&&(console.warn("THREE.Box2: .getParameter() target is now required"),b=new A);return b.set((a.x-this.min.x)/(this.max.x-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y))},intersectsBox:function(a){return a.max.xthis.max.x||a.max.ythis.max.y? +!1:!0},clampPoint:function(a,b){void 0===b&&(console.warn("THREE.Box2: .clampPoint() target is now required"),b=new A);return b.copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new A;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&& +a.max.equals(this.max)}});Object.assign(ze.prototype,{set:function(a,b){this.start.copy(a);this.end.copy(b);return this},clone:function(){return(new this.constructor).copy(this)},copy:function(a){this.start.copy(a.start);this.end.copy(a.end);return this},getCenter:function(a){void 0===a&&(console.warn("THREE.Line3: .getCenter() target is now required"),a=new p);return a.addVectors(this.start,this.end).multiplyScalar(.5)},delta:function(a){void 0===a&&(console.warn("THREE.Line3: .delta() target is now required"), +a=new p);return a.subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(a,b){void 0===b&&(console.warn("THREE.Line3: .at() target is now required"),b=new p);return this.delta(b).multiplyScalar(a).add(this.start)},closestPointToPointParameter:function(){var a=new p,b=new p;return function(c,d){a.subVectors(c,this.start);b.subVectors(this.end,this.start);c=b.dot(b);c=b.dot(a)/c;d&& +(c=S.clamp(c,0,1));return c}}(),closestPointToPoint:function(a,b,c){a=this.closestPointToPointParameter(a,b);void 0===c&&(console.warn("THREE.Line3: .closestPointToPoint() target is now required"),c=new p);return this.delta(c).multiplyScalar(a).add(this.start)},applyMatrix4:function(a){this.start.applyMatrix4(a);this.end.applyMatrix4(a);return this},equals:function(a){return a.start.equals(this.start)&&a.end.equals(this.end)}});jd.prototype=Object.create(C.prototype);jd.prototype.constructor=jd;jd.prototype.isImmediateRenderObject= +!0;kd.prototype=Object.create(W.prototype);kd.prototype.constructor=kd;kd.prototype.update=function(){var a=new p,b=new p,c=new oa;return function(){var d=["a","b","c"];this.object.updateMatrixWorld(!0);c.getNormalMatrix(this.object.matrixWorld);var e=this.object.matrixWorld,f=this.geometry.attributes.position,g=this.object.geometry;if(g&&g.isGeometry)for(var h=g.vertices,k=g.faces,l=g=0,p=k.length;lMath.abs(b)&&(b=1E-8);this.scale.set(.5*this.size,.5*this.size,b);this.children[0].material.side=0>b?1:0;this.lookAt(this.plane.normal);C.prototype.updateMatrixWorld.call(this,a)};var Sd,Ae;Hb.prototype= +Object.create(C.prototype);Hb.prototype.constructor=Hb;Hb.prototype.setDirection=function(){var a=new p,b;return function(c){.99999c.y?this.quaternion.set(1,0,0,0):(a.set(c.z,0,-c.x).normalize(),b=Math.acos(c.y),this.quaternion.setFromAxisAngle(a,b))}}();Hb.prototype.setLength=function(a,b,c){void 0===b&&(b=.2*a);void 0===c&&(c=.2*b);this.line.scale.set(1,Math.max(0,a-b),1);this.line.updateMatrix();this.cone.scale.set(c,b,c);this.cone.position.y=a;this.cone.updateMatrix()}; +Hb.prototype.setColor=function(a){this.line.material.color.copy(a);this.cone.material.color.copy(a)};qd.prototype=Object.create(W.prototype);qd.prototype.constructor=qd;N.create=function(a,b){console.log("THREE.Curve.create() has been deprecated");a.prototype=Object.create(N.prototype);a.prototype.constructor=a;a.prototype.getPoint=b;return a};Object.assign(ab.prototype,{createPointsGeometry:function(a){console.warn("THREE.CurvePath: .createPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead."); +a=this.getPoints(a);return this.createGeometry(a)},createSpacedPointsGeometry:function(a){console.warn("THREE.CurvePath: .createSpacedPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.");a=this.getSpacedPoints(a);return this.createGeometry(a)},createGeometry:function(a){console.warn("THREE.CurvePath: .createGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.");for(var b=new M,c=0,d=a.length;c=this.duration){ + this.target[this.channel] = this.endValue; + if (this.oncomplete) this.oncomplete(); + this.finished = true; + }else{ + this.target[this.channel] = this.easing.value(this.currentTime); + } + } +} +class SFX{ + constructor(options){ + this.context = options.context; + const volume = (options.volume!=undefined) ? options.volume : 1.0; + this.gainNode = this.context.createGain(); + this.gainNode.gain.setValueAtTime(volume, this.context.currentTime); + this.gainNode.connect(this.context.destination); + this._loop = (options.loop==undefined) ? false : options.loop; + this.fadeDuration = (options.fadeDuration==undefined) ? 0.5 : options.fadeDuration; + this.autoplay = (options.autoplay==undefined) ? false : options.autoplay; + this.buffer = null; + + let codec; + for(let prop in options.src){ + if (SFX.supportsAudioType(prop)){ + codec = prop; + break; + } + } + + if (codec!=undefined){ + this.url = options.src[codec]; + this.load(this.url); + }else{ + console.warn("Browser does not support any of the supplied audio files"); + } + } + + static supportsAudioType(type) { + let audio; + + // Allow user to create shortcuts, i.e. just "mp3" + let formats = { + mp3: 'audio/mpeg', + wav: 'audio/wav', + aif: 'audio/x-aiff', + ogg: 'audio/ogg' + }; + + if(!audio) audio = document.createElement('audio'); + + return audio.canPlayType(formats[type] || type); + } + + load(url) { + // Load buffer asynchronously + const request = new XMLHttpRequest(); + request.open("GET", url, true); + request.responseType = "arraybuffer"; + + const sfx = this; + + request.onload = function() { + // Asynchronously decode the audio file data in request.response + sfx.context.decodeAudioData( + request.response, + function(buffer) { + if (!buffer) { + console.error('error decoding file data: ' + sfx.url); + return; + } + sfx.buffer = buffer; + if (sfx.autoplay) sfx.play(); + }, + function(error) { + console.error('decodeAudioData error', error); + } + ); + } + + request.onerror = function() { + console.error('SFX Loader: XHR error'); + } + + request.send(); + } + + set loop(value){ + this._loop = value; + if (this.source!=undefined) this.source.loop = value; + } + + play(){ + if (this.buffer==null) return; + if (this.source!=undefined) this.source.stop(); + this.source = this.context.createBufferSource(); + this.source.loop = this._loop; + this.source.buffer = this.buffer; + this.source.connect(this.gainNode); + this.source.start(0); + } + + set volume(value){ + this._volume = value; + this.gainNode.gain.setTargetAtTime(value, this.context.currentTime + this.fadeDuration, 0); + } + + pause(){ + if (this.source==undefined) return; + this.source.stop(); + } + + stop(){ + if (this.source==undefined) return; + this.source.stop(); + delete this.source; + } +} + +class JoyStick{ + constructor(options){ + const circle = document.createElement("div"); + circle.style.cssText = "position:absolute; bottom:35px; width:80px; height:80px; background:rgba(126, 126, 126, 0.5); border:#444 solid medium; border-radius:50%; left:50%; transform:translateX(-50%);"; + const thumb = document.createElement("div"); + thumb.style.cssText = "position: absolute; left: 20px; top: 20px; width: 40px; height: 40px; border-radius: 50%; background: #fff;"; + circle.appendChild(thumb); + document.body.appendChild(circle); + this.domElement = thumb; + this.maxRadius = options.maxRadius || 40; + this.maxRadiusSquared = this.maxRadius * this.maxRadius; + this.onMove = options.onMove; + this.game = options.game; + this.origin = { left:this.domElement.offsetLeft, top:this.domElement.offsetTop }; + this.rotationDamping = options.rotationDamping || 0.06; + this.moveDamping = options.moveDamping || 0.01; + if (this.domElement!=undefined){ + const joystick = this; + if ('ontouchstart' in window){ + this.domElement.addEventListener('touchstart', function(evt){ evt.preventDefault(); joystick.tap(evt); evt.stopPropagation();}); + }else{ + this.domElement.addEventListener('mousedown', function(evt){ evt.preventDefault(); joystick.tap(evt); evt.stopPropagation();}); + } + } + } + + getMousePosition(evt){ + let clientX = evt.targetTouches ? evt.targetTouches[0].pageX : evt.clientX; + let clientY = evt.targetTouches ? evt.targetTouches[0].pageY : evt.clientY; + return { x:clientX, y:clientY }; + } + + tap(evt){ + evt = evt || window.event; + // get the mouse cursor position at startup: + this.offset = this.getMousePosition(evt); + const joystick = this; + if ('ontouchstart' in window){ + document.ontouchmove = function(evt){ evt.preventDefault(); joystick.move(evt); }; + document.ontouchend = function(evt){ evt.preventDefault(); joystick.up(evt); }; + }else{ + document.onmousemove = function(evt){ evt.preventDefault(); joystick.move(evt); }; + document.onmouseup = function(evt){ evt.preventDefault(); joystick.up(evt); }; + } + } + + move(evt){ + evt = evt || window.event; + const mouse = this.getMousePosition(evt); + // calculate the new cursor position: + let left = mouse.x - this.offset.x; + let top = mouse.y - this.offset.y; + //this.offset = mouse; + + const sqMag = left*left + top*top; + if (sqMag>this.maxRadiusSquared){ + //Only use sqrt if essential + const magnitude = Math.sqrt(sqMag); + left /= magnitude; + top /= magnitude; + left *= this.maxRadius; + top *= this.maxRadius; + } + // set the element's new position: + this.domElement.style.top = `${top + this.domElement.clientHeight/2}px`; + this.domElement.style.left = `${left + this.domElement.clientWidth/2}px`; + + const forward = -(top - this.origin.top + this.domElement.clientHeight/2)/this.maxRadius; + const turn = (left - this.origin.left + this.domElement.clientWidth/2)/this.maxRadius; + + if (this.onMove!=undefined) this.onMove.call(this.game, forward, turn); + } + + up(evt){ + if ('ontouchstart' in window){ + document.ontouchmove = null; + document.touchend = null; + }else{ + document.onmousemove = null; + document.onmouseup = null; + } + this.domElement.style.top = `${this.origin.top}px`; + this.domElement.style.left = `${this.origin.left}px`; + + this.onMove.call(this.game, 0, 0); + } +} + +class Preloader{ + constructor(options){ + this.assets = {}; + for(let asset of options.assets){ + this.assets[asset] = { loaded:0, complete:false }; + this.load(asset); + } + this.container = options.container; + + if (options.onprogress==undefined){ + this.onprogress = onprogress; + this.domElement = document.createElement("div"); + this.domElement.style.position = 'absolute'; + this.domElement.style.top = '0'; + this.domElement.style.left = '0'; + this.domElement.style.width = '100%'; + this.domElement.style.height = '100%'; + this.domElement.style.background = '#000'; + this.domElement.style.opacity = '0.7'; + this.domElement.style.display = 'flex'; + this.domElement.style.alignItems = 'center'; + this.domElement.style.justifyContent = 'center'; + this.domElement.style.zIndex = '1111'; + const barBase = document.createElement("div"); + barBase.style.background = '#aaa'; + barBase.style.width = '50%'; + barBase.style.minWidth = '250px'; + barBase.style.borderRadius = '10px'; + barBase.style.height = '15px'; + this.domElement.appendChild(barBase); + const bar = document.createElement("div"); + bar.style.background = '#2a2'; + bar.style.width = '50%'; + bar.style.borderRadius = '10px'; + bar.style.height = '100%'; + bar.style.width = '0'; + barBase.appendChild(bar); + this.progressBar = bar; + if (this.container!=undefined){ + this.container.appendChild(this.domElement); + }else{ + document.body.appendChild(this.domElement); + } + }else{ + this.onprogress = options.onprogress; + } + + this.oncomplete = options.oncomplete; + + const loader = this; + function onprogress(delta){ + const progress = delta*100; + loader.progressBar.style.width = `${progress}%`; + } + } + + checkCompleted(){ + for(let prop in this.assets){ + const asset = this.assets[prop]; + if (!asset.complete) return false; + } + return true; + } + + get progress(){ + let total = 0; + let loaded = 0; + + for(let prop in this.assets){ + const asset = this.assets[prop]; + if (asset.total == undefined){ + loaded = 0; + break; + } + loaded += asset.loaded; + total += asset.total; + } + + return loaded/total; + } + + load(url){ + const loader = this; + var xobj = new XMLHttpRequest(); + xobj.overrideMimeType("application/json"); + xobj.open('GET', url, true); + xobj.onreadystatechange = function () { + if (xobj.readyState == 4 && xobj.status == "200") { + loader.assets[url].complete = true; + if (loader.checkCompleted()){ + if (loader.domElement!=undefined){ + if (loader.container!=undefined){ + loader.container.removeChild(loader.domElement); + }else{ + document.body.removeChild(loader.domElement); + } + } + loader.oncomplete(); + } + } + }; + xobj.onprogress = function(e){ + const asset = loader.assets[url]; + asset.loaded = e.loaded; + asset.total = e.total; + loader.onprogress(loader.progress); + } + xobj.send(null); + } +} \ No newline at end of file diff --git a/daydream/public/style.css b/daydream/public/style.css new file mode 100644 index 000000000..6ec4aad1a --- /dev/null +++ b/daydream/public/style.css @@ -0,0 +1,48 @@ +body { + font-family: Monospace; + background-color: #000; + color: #fff; + margin: 0px; + overflow: hidden; +} +#message{ + position:absolute; + left: 50%; + top: 50%; + transform: translateX(-50%) translateY(-50%); + font-family: 'Kavivanar', san-serif; + font-size: 40px; + width:80%; + max-width:280px; + padding: 15px 15px 50px 15px; + text-align: center; + display:none; + align-content: center; + justify-content: center; + background-color: aliceblue; + border: medium solid #3D455C; + border-radius: 20px; + color: #3D455C; + z-index: 99; +} +#message_ok{ + position: absolute; + bottom: 10px; + padding: 10px 20px 10px 20px; + font-family: sans-serif; + font-size: 20px; + background-color: #3D455C; + color: #fff; + border-radius: 8px; + border: medium solid #696882; +} +#chat{ + position: absolute; + width: 100%; + height: 40px; + bottom: -40px; + transition: all 0.5s; +} +form { background: #000; padding: 3px; width: 100%; } +form input { border: 0; padding: 10px; width: 88%; margin-right: .5%; } +form button { width: 8%; background: rgb(130, 224, 255); border: none; padding: 10px; } \ No newline at end of file diff --git a/daydream/server.js b/daydream/server.js new file mode 100644 index 000000000..8c7e0d5ad --- /dev/null +++ b/daydream/server.js @@ -0,0 +1,82 @@ +const express = require('express'), + app = express(), + http = require('http').Server(app), + io = require('socket.io')(http), + compression = require('compression'), + helmet = require('helmet'); + port = 3001; + +app.use(express.static('../daydream/public')); +app.use(express.static('../daydream/public/libs')); +app.use(compression()); +app.use(helmet()); + +app.get('/',function(req, res) { + res.sendFile(__dirname + '../daydream/public/index.html'); +}); + +io.sockets.on('connection', function(socket){ + socket.userData = { x:0, y:0, z:0, heading:0 };//Default values; + + console.log(`${socket.id} connected`); + socket.emit('setId', { id:socket.id }); + + socket.on('disconnect', function(){ + socket.broadcast.emit('deletePlayer', { id: socket.id }); + }); + + socket.on('init', function(data){ + console.log(`socket.init ${data.model}`); + socket.userData.model = data.model; + socket.userData.colour = data.colour; + socket.userData.x = data.x; + socket.userData.y = data.y; + socket.userData.z = data.z; + socket.userData.heading = data.h; + socket.userData.pb = data.pb, + socket.userData.action = "Idle"; + }); + + socket.on('update', function(data){ + socket.userData.x = data.x; + socket.userData.y = data.y; + socket.userData.z = data.z; + socket.userData.heading = data.h; + socket.userData.pb = data.pb, + socket.userData.action = data.action; + }); + + socket.on('chat message', function(data){ + console.log(`chat message:${data.id} ${data.message}`); + io.to(data.id).emit('chat message', { id: socket.id, message: data.message }); + }) +}); + +http.listen(port, process.env.IP, () => { + console.log('Game has started!'); + console.log('Port:', port); +}); + +setInterval(function(){ + const nsp = io.of('/'); + let pack = []; + + for(let id in io.sockets.sockets){ + const socket = nsp.connected[id]; + //Only push sockets that have been initialised + if (socket.userData.model!==undefined){ + pack.push({ + id: socket.id, + model: socket.userData.model, + colour: socket.userData.colour, + x: socket.userData.x, + y: socket.userData.y, + z: socket.userData.z, + heading: socket.userData.heading, + pb: socket.userData.pb, + action: socket.userData.action + }); + } + } + if (pack.length>0) io.emit('remoteData', pack); +}, 40);