diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..482e34b1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.DS_Store
+*.swp
+*.swo
diff --git a/CNAME b/CNAME
new file mode 100644
index 00000000..a76d3154
--- /dev/null
+++ b/CNAME
@@ -0,0 +1 @@
+photovrse.com
diff --git a/CONTRIBUTING b/CONTRIBUTING
new file mode 100644
index 00000000..43b6883e
--- /dev/null
+++ b/CONTRIBUTING
@@ -0,0 +1,22 @@
+# Building
+
+This project uses browserify to manage dependencies and build. Watchify is
+especially convenient to preserve the write-and-reload model of development.
+This package lives in the npm index. There is also a bower version of it, but it
+is deprecated.
+
+Relevant commands:
+
+ npm build - builds the module.
+ npm build-analytics - builds the module with analytics support.
+ npm watch - auto-builds the module whenever any source changes.
+
+
+# Updating the npm entry
+
+Once changes are made, a new version can be published to the index using the
+following commands:
+
+ npm version
+ npm publish
+ git push
diff --git a/COPYING b/COPYING
new file mode 100644
index 00000000..dfc59b7f
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2015 Google Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..80508426
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+VR Views
+========
+
+Please read the documentation available at
+.
diff --git a/build/device-motion-sender.min.js b/build/device-motion-sender.min.js
new file mode 100644
index 00000000..d43248e9
--- /dev/null
+++ b/build/device-motion-sender.min.js
@@ -0,0 +1 @@
+function DeviceMotionSender(){if(!this.isIOS_()){return}window.addEventListener("devicemotion",this.onDeviceMotion_.bind(this),false);this.iframes=document.querySelectorAll("iframe.vrview")}DeviceMotionSender.prototype.onDeviceMotion_=function(e){var message={type:"DeviceMotion",deviceMotionEvent:this.cloneDeviceMotionEvent_(e)};for(var i=0;it;t+=2){var e=X[t],n=X[t+1];e(n),X[t]=void 0,X[t+1]=void 0}G=0}function f(){try{var t=_dereq_,e=t("vertx");return U=e.runOnLoop||e.runOnContext,i()}catch(n){return c()}}function l(t,e){var n=this,r=n._state;if(r===et&&!t||r===nt&&!e)return this;var o=new this.constructor(p),i=n._result;if(r){var s=arguments[r-1];H(function(){C(r,o,s,i)})}else j(n,o,t,e);return o}function h(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(p);return g(n,t),n}function p(){}function _(){return new TypeError("You cannot resolve a promise with itself")}function v(){return new TypeError("A promises callback cannot return that same promise.")}function d(t){try{return t.then}catch(e){return rt.error=e,rt}}function y(t,e,n,r){try{t.call(e,n,r)}catch(o){return o}}function m(t,e,n){H(function(t){var r=!1,o=y(n,e,function(n){r||(r=!0,e!==n?g(t,n):E(t,n))},function(e){r||(r=!0,S(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&o&&(r=!0,S(t,o))},t)}function w(t,e){e._state===et?E(t,e._result):e._state===nt?S(t,e._result):j(e,void 0,function(e){g(t,e)},function(e){S(t,e)})}function b(t,n,r){n.constructor===t.constructor&&r===Z&&constructor.resolve===$?w(t,n):r===rt?S(t,rt.error):void 0===r?E(t,n):e(r)?m(t,n,r):E(t,n)}function g(e,n){e===n?S(e,_()):t(n)?b(e,n,d(n)):E(e,n)}function A(t){t._onerror&&t._onerror(t._result),T(t)}function E(t,e){t._state===tt&&(t._result=e,t._state=et,0!==t._subscribers.length&&H(T,t))}function S(t,e){t._state===tt&&(t._state=nt,t._result=e,H(A,t))}function j(t,e,n,r){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+et]=n,o[i+nt]=r,0===i&&t._state&&H(T,t)}function T(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,o,i=t._result,s=0;ss;s++)j(r.resolve(t[s]),void 0,e,n);return o}function Y(t){var e=this,n=new e(p);return S(n,t),n}function q(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function F(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function D(t){this._id=ct++,this._state=void 0,this._result=void 0,this._subscribers=[],p!==t&&("function"!=typeof t&&q(),this instanceof D?M(this,t):F())}function K(t,e){this._instanceConstructor=t,this.promise=new t(p),Array.isArray(e)?(this._input=e,this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?E(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&E(this.promise,this._result))):S(this.promise,this._validationError())}function L(){var t;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;(!n||"[object Promise]"!==Object.prototype.toString.call(n.resolve())||n.cast)&&(t.Promise=at)}var N;N=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var U,W,z,B=N,G=0,H=function(t,e){X[G]=t,X[G+1]=e,G+=2,2===G&&(W?W(a):z())},I="undefined"!=typeof window?window:void 0,J=I||{},Q=J.MutationObserver||J.WebKitMutationObserver,R="undefined"!=typeof process&&"[object process]"==={}.toString.call(process),V="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,X=new Array(1e3);z=R?o():Q?s():V?u():void 0===I&&"function"==typeof _dereq_?f():c();var Z=l,$=h,tt=void 0,et=1,nt=2,rt=new P,ot=new P,it=O,st=k,ut=Y,ct=0,at=D;D.all=it,D.race=st,D.resolve=$,D.reject=ut,D._setScheduler=n,D._setAsap=r,D._asap=H,D.prototype={constructor:D,then:Z,"catch":function(t){return this.then(null,t)}};var ft=K;K.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},K.prototype._enumerate=function(){for(var t=this.length,e=this._input,n=0;this._state===tt&&t>n;n++)this._eachEntry(e[n],n)},K.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===$){var o=d(t);if(o===Z&&t._state!==tt)this._settledAt(t._state,e,t._result);else if("function"!=typeof o)this._remaining--,this._result[e]=t;else if(n===at){var i=new n(p);b(i,t,o),this._willSettleAt(i,e)}else this._willSettleAt(new n(function(e){e(t)}),e)}else this._willSettleAt(r(t),e)},K.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===tt&&(this._remaining--,t===nt?S(r,n):this._result[e]=n),0===this._remaining&&E(r,this._result)},K.prototype._willSettleAt=function(t,e){var n=this;j(t,void 0,function(t){n._settledAt(et,e,t)},function(t){n._settledAt(nt,e,t)})};var lt=L,ht={Promise:at,polyfill:lt};"function"==typeof define&&define.amd?define(function(){return ht}):"undefined"!=typeof module&&module.exports?module.exports=ht:"undefined"!=typeof this&&(this.ES6Promise=ht),lt()}).call(this);
+}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"_process":20}],2:[function(_dereq_,module,exports){
+// stats.js - http://github.com/mrdoob/stats.js
+var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";
+i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div");
+k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display=
+"block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:12,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height=
+a+"px",m=b,r=0);return b},update:function(){l=this.end()}}};"object"===typeof module&&(module.exports=Stats);
+
+},{}],3:[function(_dereq_,module,exports){
+/**
+ * @author dmarcos / https://github.com/dmarcos
+ * @author mrdoob / http://mrdoob.com
+ */
+
+var VRControls = function ( object, onError ) {
+
+ var scope = this;
+
+ var vrInputs = [];
+
+ function filterInvalidDevices( devices ) {
+
+ // Exclude Cardboard position sensor if Oculus exists.
+
+ var oculusDevices = devices.filter( function ( device ) {
+
+ return device.deviceName.toLowerCase().indexOf( 'oculus' ) !== - 1;
+
+ } );
+
+ if ( oculusDevices.length >= 1 ) {
+
+ return devices.filter( function ( device ) {
+
+ return device.deviceName.toLowerCase().indexOf( 'cardboard' ) === - 1;
+
+ } );
+
+ } else {
+
+ return devices;
+
+ }
+
+ }
+
+ function gotVRDevices( devices ) {
+
+ devices = filterInvalidDevices( devices );
+
+ for ( var i = 0; i < devices.length; i ++ ) {
+
+ if ( devices[ i ] instanceof PositionSensorVRDevice ) {
+
+ vrInputs.push( devices[ i ] );
+
+ }
+
+ }
+
+ if ( onError ) onError( 'HMD not available' );
+
+ }
+
+ if ( navigator.getVRDevices ) {
+
+ navigator.getVRDevices().then( gotVRDevices );
+
+ }
+
+ // the Rift SDK returns the position in meters
+ // this scale factor allows the user to define how meters
+ // are converted to scene units.
+
+ this.scale = 1;
+
+ this.update = function () {
+
+ for ( var i = 0; i < vrInputs.length; i ++ ) {
+
+ var vrInput = vrInputs[ i ];
+
+ var state = vrInput.getState();
+
+ if ( state.orientation !== null ) {
+
+ object.quaternion.copy( state.orientation );
+
+ }
+
+ if ( state.position !== null ) {
+
+ object.position.copy( state.position ).multiplyScalar( scope.scale );
+
+ }
+
+ }
+
+ };
+
+ this.resetSensor = function () {
+
+ for ( var i = 0; i < vrInputs.length; i ++ ) {
+
+ var vrInput = vrInputs[ i ];
+
+ if ( vrInput.resetSensor !== undefined ) {
+
+ vrInput.resetSensor();
+
+ } else if ( vrInput.zeroSensor !== undefined ) {
+
+ vrInput.zeroSensor();
+
+ }
+
+ }
+
+ };
+
+ this.zeroSensor = function () {
+
+ console.warn( 'THREE.VRControls: .zeroSensor() is now .resetSensor().' );
+ this.resetSensor();
+
+ };
+
+ this.dispose = function () {
+
+ vrInputs = [];
+
+ };
+
+};
+
+try {
+ module.exports = VRControls;
+} catch (e) {
+ THREE.VRControls = VRControls;
+}
+
+},{}],4:[function(_dereq_,module,exports){
+/**
+ * @author dmarcos / https://github.com/dmarcos
+ * @author mrdoob / http://mrdoob.com
+ *
+ * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html
+ *
+ * Firefox: http://mozvr.com/downloads/
+ * Chromium: https://drive.google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list
+ *
+ */
+
+var VREffect = function ( renderer, onError ) {
+
+ var vrHMD;
+ var eyeTranslationL, eyeFOVL, rectL;
+ var eyeTranslationR, eyeFOVR, rectR;
+
+ function gotVRDevices( devices ) {
+
+ for ( var i = 0; i < devices.length; i ++ ) {
+
+ if ( devices[ i ] instanceof HMDVRDevice ) {
+
+ vrHMD = devices[ i ];
+
+ break; // We keep the first we encounter
+
+ }
+
+ }
+
+ if ( vrHMD === undefined ) {
+
+ if ( onError ) onError( 'HMD not available' );
+
+ }
+
+ }
+
+ if ( navigator.getVRDevices ) {
+
+ navigator.getVRDevices().then( gotVRDevices );
+
+ }
+
+ //
+
+ this.scale = 1;
+
+ this.setSize = function( width, height ) {
+
+ renderer.setSize( width, height );
+
+ };
+
+ // fullscreen
+
+ var isFullscreen = false;
+
+ var canvas = renderer.domElement;
+ var fullscreenchange = canvas.mozRequestFullScreen ? 'mozfullscreenchange' : 'webkitfullscreenchange';
+
+ document.addEventListener( fullscreenchange, function ( event ) {
+
+ isFullscreen = document.mozFullScreenElement || document.webkitFullscreenElement;
+
+ }, false );
+
+ this.setFullScreen = function ( boolean ) {
+
+ if ( vrHMD === undefined ) return;
+ if ( isFullscreen === boolean ) return;
+
+ if ( canvas.mozRequestFullScreen ) {
+
+ canvas.mozRequestFullScreen( { vrDisplay: vrHMD } );
+
+ } else if ( canvas.webkitRequestFullscreen ) {
+
+ canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } );
+
+ }
+
+ };
+
+ // render
+
+ var cameraL = new THREE.PerspectiveCamera();
+ var cameraR = new THREE.PerspectiveCamera();
+
+ this.render = function ( scene, camera ) {
+
+ if ( vrHMD ) {
+
+ var eyeParamsL = vrHMD.getEyeParameters( 'left' );
+ var eyeParamsR = vrHMD.getEyeParameters( 'right' );
+
+ eyeTranslationL = eyeParamsL.eyeTranslation;
+ eyeTranslationR = eyeParamsR.eyeTranslation;
+ eyeFOVL = eyeParamsL.recommendedFieldOfView;
+ eyeFOVR = eyeParamsR.recommendedFieldOfView;
+ rectL = eyeParamsL.renderRect;
+ rectR = eyeParamsR.renderRect;
+
+ var sceneL, sceneR;
+
+ if ( Array.isArray( scene ) ) {
+
+ sceneL = scene[ 0 ];
+ sceneR = scene[ 1 ];
+
+ } else {
+
+ sceneL = scene;
+ sceneR = scene;
+
+ }
+
+ var size = renderer.getSize();
+ size.width /= 2;
+
+ renderer.enableScissorTest( true );
+ renderer.clear();
+
+ if ( camera.parent === null ) camera.updateMatrixWorld();
+
+ cameraL.projectionMatrix = fovToProjection( eyeFOVL, true, camera.near, camera.far );
+ cameraR.projectionMatrix = fovToProjection( eyeFOVR, true, camera.near, camera.far );
+
+ camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale );
+ camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale );
+
+ cameraL.translateX( eyeTranslationL.x * this.scale );
+ cameraR.translateX( eyeTranslationR.x * this.scale );
+
+ // render left eye
+ if ( rectL ) {
+
+ renderer.setViewport( rectL.x, rectL.y, rectL.width, rectL.height );
+ renderer.setScissor( rectL.x, rectL.y, rectL.width, rectL.height );
+
+ } else {
+
+ renderer.setViewport( 0, 0, size.width, size.height );
+ renderer.setScissor( 0, 0, size.width, size.height );
+
+ }
+ renderer.render( sceneL, cameraL );
+
+ // render right eye
+ if ( rectR ) {
+
+ renderer.setViewport( rectR.x, rectR.y, rectR.width, rectR.height );
+ renderer.setScissor( rectR.x, rectR.y, rectR.width, rectR.height );
+
+ } else {
+
+ renderer.setViewport( size.width, 0, size.width, size.height );
+ renderer.setScissor( size.width, 0, size.width, size.height );
+
+ }
+ renderer.render( sceneR, cameraR );
+
+ renderer.enableScissorTest( false );
+
+ return;
+
+ }
+
+ // Regular render mode if not HMD
+
+ if ( Array.isArray( scene ) ) scene = scene[ 0 ];
+
+ renderer.render( scene, camera );
+
+ };
+
+ //
+
+ function fovToNDCScaleOffset( fov ) {
+
+ var pxscale = 2.0 / ( fov.leftTan + fov.rightTan );
+ var pxoffset = ( fov.leftTan - fov.rightTan ) * pxscale * 0.5;
+ var pyscale = 2.0 / ( fov.upTan + fov.downTan );
+ var pyoffset = ( fov.upTan - fov.downTan ) * pyscale * 0.5;
+ return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] };
+
+ }
+
+ function fovPortToProjection( fov, rightHanded, zNear, zFar ) {
+
+ rightHanded = rightHanded === undefined ? true : rightHanded;
+ zNear = zNear === undefined ? 0.01 : zNear;
+ zFar = zFar === undefined ? 10000.0 : zFar;
+
+ var handednessScale = rightHanded ? - 1.0 : 1.0;
+
+ // start with an identity matrix
+ var mobj = new THREE.Matrix4();
+ var m = mobj.elements;
+
+ // and with scale/offset info for normalized device coords
+ var scaleAndOffset = fovToNDCScaleOffset( fov );
+
+ // X result, map clip edges to [-w,+w]
+ m[ 0 * 4 + 0 ] = scaleAndOffset.scale[ 0 ];
+ m[ 0 * 4 + 1 ] = 0.0;
+ m[ 0 * 4 + 2 ] = scaleAndOffset.offset[ 0 ] * handednessScale;
+ m[ 0 * 4 + 3 ] = 0.0;
+
+ // Y result, map clip edges to [-w,+w]
+ // Y offset is negated because this proj matrix transforms from world coords with Y=up,
+ // but the NDC scaling has Y=down (thanks D3D?)
+ m[ 1 * 4 + 0 ] = 0.0;
+ m[ 1 * 4 + 1 ] = scaleAndOffset.scale[ 1 ];
+ m[ 1 * 4 + 2 ] = - scaleAndOffset.offset[ 1 ] * handednessScale;
+ m[ 1 * 4 + 3 ] = 0.0;
+
+ // Z result (up to the app)
+ m[ 2 * 4 + 0 ] = 0.0;
+ m[ 2 * 4 + 1 ] = 0.0;
+ m[ 2 * 4 + 2 ] = zFar / ( zNear - zFar ) * - handednessScale;
+ m[ 2 * 4 + 3 ] = ( zFar * zNear ) / ( zNear - zFar );
+
+ // W result (= Z in)
+ m[ 3 * 4 + 0 ] = 0.0;
+ m[ 3 * 4 + 1 ] = 0.0;
+ m[ 3 * 4 + 2 ] = handednessScale;
+ m[ 3 * 4 + 3 ] = 0.0;
+
+ mobj.transpose();
+
+ return mobj;
+
+ }
+
+ function fovToProjection( fov, rightHanded, zNear, zFar ) {
+
+ var DEG2RAD = Math.PI / 180.0;
+
+ var fovPort = {
+ upTan: Math.tan( fov.upDegrees * DEG2RAD ),
+ downTan: Math.tan( fov.downDegrees * DEG2RAD ),
+ leftTan: Math.tan( fov.leftDegrees * DEG2RAD ),
+ rightTan: Math.tan( fov.rightDegrees * DEG2RAD )
+ };
+
+ return fovPortToProjection( fovPort, rightHanded, zNear, zFar );
+
+ }
+
+};
+
+try {
+ module.exports = VREffect;
+} catch (e) {
+ THREE.VREffect = VREffect;
+}
+
+},{}],5:[function(_dereq_,module,exports){
+var self = self || {};// File:src/Three.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+var THREE = { REVISION: '73' };
+
+//
+
+if ( typeof define === 'function' && define.amd ) {
+
+ define( 'three', THREE );
+
+} else if ( 'undefined' !== typeof exports && 'undefined' !== typeof module ) {
+
+ module.exports = THREE;
+
+}
+
+
+// polyfills
+
+if ( self.requestAnimationFrame === undefined || self.cancelAnimationFrame === undefined ) {
+
+ // Missing in Android stock browser.
+
+ ( function () {
+
+ var lastTime = 0;
+ var vendors = [ 'ms', 'moz', 'webkit', 'o' ];
+
+ for ( var x = 0; x < vendors.length && ! self.requestAnimationFrame; ++ x ) {
+
+ self.requestAnimationFrame = self[ vendors[ x ] + 'RequestAnimationFrame' ];
+ self.cancelAnimationFrame = self[ vendors[ x ] + 'CancelAnimationFrame' ] || self[ vendors[ x ] + 'CancelRequestAnimationFrame' ];
+
+ }
+
+ if ( self.requestAnimationFrame === undefined && self.setTimeout !== undefined ) {
+
+ self.requestAnimationFrame = function ( callback ) {
+
+ var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
+ var id = self.setTimeout( function () {
+
+ callback( currTime + timeToCall );
+
+ }, timeToCall );
+ lastTime = currTime + timeToCall;
+ return id;
+
+ };
+
+ }
+
+ if ( self.cancelAnimationFrame === undefined && self.clearTimeout !== undefined ) {
+
+ self.cancelAnimationFrame = function ( id ) {
+
+ self.clearTimeout( id );
+
+ };
+
+ }
+
+ } )();
+
+}
+
+//
+
+if ( self.performance === undefined ) {
+
+ self.performance = {};
+
+}
+
+if ( self.performance.now === undefined ) {
+
+ ( function () {
+
+ var start = Date.now();
+
+ self.performance.now = function () {
+
+ return Date.now() - start;
+
+ }
+
+ } )();
+
+}
+
+//
+
+if ( Number.EPSILON === undefined ) {
+
+ Number.EPSILON = Math.pow( 2, -52 );
+
+}
+
+//
+
+if ( Math.sign === undefined ) {
+
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
+
+ Math.sign = function ( x ) {
+
+ return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x;
+
+ };
+
+}
+
+if ( Function.prototype.name === undefined && Object.defineProperty !== undefined ) {
+
+ // Missing in IE9-11.
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
+
+ Object.defineProperty( Function.prototype, 'name', {
+
+ get: function () {
+
+ return this.toString().match( /^\s*function\s*(\S*)\s*\(/ )[ 1 ];
+
+ }
+
+ } );
+
+}
+
+// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.button
+
+THREE.MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
+
+// GL STATE CONSTANTS
+
+THREE.CullFaceNone = 0;
+THREE.CullFaceBack = 1;
+THREE.CullFaceFront = 2;
+THREE.CullFaceFrontBack = 3;
+
+THREE.FrontFaceDirectionCW = 0;
+THREE.FrontFaceDirectionCCW = 1;
+
+// SHADOWING TYPES
+
+THREE.BasicShadowMap = 0;
+THREE.PCFShadowMap = 1;
+THREE.PCFSoftShadowMap = 2;
+
+// MATERIAL CONSTANTS
+
+// side
+
+THREE.FrontSide = 0;
+THREE.BackSide = 1;
+THREE.DoubleSide = 2;
+
+// shading
+
+THREE.FlatShading = 1;
+THREE.SmoothShading = 2;
+
+// colors
+
+THREE.NoColors = 0;
+THREE.FaceColors = 1;
+THREE.VertexColors = 2;
+
+// blending modes
+
+THREE.NoBlending = 0;
+THREE.NormalBlending = 1;
+THREE.AdditiveBlending = 2;
+THREE.SubtractiveBlending = 3;
+THREE.MultiplyBlending = 4;
+THREE.CustomBlending = 5;
+
+// custom blending equations
+// (numbers start from 100 not to clash with other
+// mappings to OpenGL constants defined in Texture.js)
+
+THREE.AddEquation = 100;
+THREE.SubtractEquation = 101;
+THREE.ReverseSubtractEquation = 102;
+THREE.MinEquation = 103;
+THREE.MaxEquation = 104;
+
+// custom blending destination factors
+
+THREE.ZeroFactor = 200;
+THREE.OneFactor = 201;
+THREE.SrcColorFactor = 202;
+THREE.OneMinusSrcColorFactor = 203;
+THREE.SrcAlphaFactor = 204;
+THREE.OneMinusSrcAlphaFactor = 205;
+THREE.DstAlphaFactor = 206;
+THREE.OneMinusDstAlphaFactor = 207;
+
+// custom blending source factors
+
+//THREE.ZeroFactor = 200;
+//THREE.OneFactor = 201;
+//THREE.SrcAlphaFactor = 204;
+//THREE.OneMinusSrcAlphaFactor = 205;
+//THREE.DstAlphaFactor = 206;
+//THREE.OneMinusDstAlphaFactor = 207;
+THREE.DstColorFactor = 208;
+THREE.OneMinusDstColorFactor = 209;
+THREE.SrcAlphaSaturateFactor = 210;
+
+// depth modes
+
+THREE.NeverDepth = 0;
+THREE.AlwaysDepth = 1;
+THREE.LessDepth = 2;
+THREE.LessEqualDepth = 3;
+THREE.EqualDepth = 4;
+THREE.GreaterEqualDepth = 5;
+THREE.GreaterDepth = 6;
+THREE.NotEqualDepth = 7;
+
+
+// TEXTURE CONSTANTS
+
+THREE.MultiplyOperation = 0;
+THREE.MixOperation = 1;
+THREE.AddOperation = 2;
+
+// Mapping modes
+
+THREE.UVMapping = 300;
+
+THREE.CubeReflectionMapping = 301;
+THREE.CubeRefractionMapping = 302;
+
+THREE.EquirectangularReflectionMapping = 303;
+THREE.EquirectangularRefractionMapping = 304;
+
+THREE.SphericalReflectionMapping = 305;
+
+// Wrapping modes
+
+THREE.RepeatWrapping = 1000;
+THREE.ClampToEdgeWrapping = 1001;
+THREE.MirroredRepeatWrapping = 1002;
+
+// Filters
+
+THREE.NearestFilter = 1003;
+THREE.NearestMipMapNearestFilter = 1004;
+THREE.NearestMipMapLinearFilter = 1005;
+THREE.LinearFilter = 1006;
+THREE.LinearMipMapNearestFilter = 1007;
+THREE.LinearMipMapLinearFilter = 1008;
+
+// Data types
+
+THREE.UnsignedByteType = 1009;
+THREE.ByteType = 1010;
+THREE.ShortType = 1011;
+THREE.UnsignedShortType = 1012;
+THREE.IntType = 1013;
+THREE.UnsignedIntType = 1014;
+THREE.FloatType = 1015;
+THREE.HalfFloatType = 1025;
+
+// Pixel types
+
+//THREE.UnsignedByteType = 1009;
+THREE.UnsignedShort4444Type = 1016;
+THREE.UnsignedShort5551Type = 1017;
+THREE.UnsignedShort565Type = 1018;
+
+// Pixel formats
+
+THREE.AlphaFormat = 1019;
+THREE.RGBFormat = 1020;
+THREE.RGBAFormat = 1021;
+THREE.LuminanceFormat = 1022;
+THREE.LuminanceAlphaFormat = 1023;
+// THREE.RGBEFormat handled as THREE.RGBAFormat in shaders
+THREE.RGBEFormat = THREE.RGBAFormat; //1024;
+
+// DDS / ST3C Compressed texture formats
+
+THREE.RGB_S3TC_DXT1_Format = 2001;
+THREE.RGBA_S3TC_DXT1_Format = 2002;
+THREE.RGBA_S3TC_DXT3_Format = 2003;
+THREE.RGBA_S3TC_DXT5_Format = 2004;
+
+
+// PVRTC compressed texture formats
+
+THREE.RGB_PVRTC_4BPPV1_Format = 2100;
+THREE.RGB_PVRTC_2BPPV1_Format = 2101;
+THREE.RGBA_PVRTC_4BPPV1_Format = 2102;
+THREE.RGBA_PVRTC_2BPPV1_Format = 2103;
+
+// Loop styles for AnimationAction
+
+THREE.LoopOnce = 2200;
+THREE.LoopRepeat = 2201;
+THREE.LoopPingPong = 2202;
+
+// DEPRECATED
+
+THREE.Projector = function () {
+
+ console.error( 'THREE.Projector has been moved to /examples/js/renderers/Projector.js.' );
+
+ this.projectVector = function ( vector, camera ) {
+
+ console.warn( 'THREE.Projector: .projectVector() is now vector.project().' );
+ vector.project( camera );
+
+ };
+
+ this.unprojectVector = function ( vector, camera ) {
+
+ console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' );
+ vector.unproject( camera );
+
+ };
+
+ this.pickingRay = function ( vector, camera ) {
+
+ console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' );
+
+ };
+
+};
+
+THREE.CanvasRenderer = function () {
+
+ console.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' );
+
+ this.domElement = document.createElement( 'canvas' );
+ this.clear = function () {};
+ this.render = function () {};
+ this.setClearColor = function () {};
+ this.setSize = function () {};
+
+};
+
+// File:src/math/Color.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Color = function ( color ) {
+
+ if ( arguments.length === 3 ) {
+
+ return this.fromArray( arguments );
+
+ }
+
+ return this.set( color );
+
+};
+
+THREE.Color.prototype = {
+
+ constructor: THREE.Color,
+
+ r: 1, g: 1, b: 1,
+
+ set: function ( value ) {
+
+ if ( value instanceof THREE.Color ) {
+
+ this.copy( value );
+
+ } else if ( typeof value === 'number' ) {
+
+ this.setHex( value );
+
+ } else if ( typeof value === 'string' ) {
+
+ this.setStyle( value );
+
+ }
+
+ return this;
+
+ },
+
+ setHex: function ( hex ) {
+
+ hex = Math.floor( hex );
+
+ this.r = ( hex >> 16 & 255 ) / 255;
+ this.g = ( hex >> 8 & 255 ) / 255;
+ this.b = ( hex & 255 ) / 255;
+
+ return this;
+
+ },
+
+ setRGB: function ( r, g, b ) {
+
+ this.r = r;
+ this.g = g;
+ this.b = b;
+
+ return this;
+
+ },
+
+ setHSL: function () {
+
+ function hue2rgb( p, q, t ) {
+
+ if ( t < 0 ) t += 1;
+ if ( t > 1 ) t -= 1;
+ if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;
+ if ( t < 1 / 2 ) return q;
+ if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t );
+ return p;
+
+ }
+
+ return function ( h, s, l ) {
+
+ // h,s,l ranges are in 0.0 - 1.0
+ h = THREE.Math.euclideanModulo( h, 1 );
+ s = THREE.Math.clamp( s, 0, 1 );
+ l = THREE.Math.clamp( l, 0, 1 );
+
+ if ( s === 0 ) {
+
+ this.r = this.g = this.b = l;
+
+ } else {
+
+ var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );
+ var q = ( 2 * l ) - p;
+
+ this.r = hue2rgb( q, p, h + 1 / 3 );
+ this.g = hue2rgb( q, p, h );
+ this.b = hue2rgb( q, p, h - 1 / 3 );
+
+ }
+
+ return this;
+
+ };
+
+ }(),
+
+ setStyle: function ( style ) {
+
+ function handleAlpha( string ) {
+
+ if ( string === undefined ) return;
+
+ if ( parseFloat( string ) < 1 ) {
+
+ console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' );
+
+ }
+
+ }
+
+
+ var m;
+
+ if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) {
+
+ // rgb / hsl
+
+ var color;
+ var name = m[ 1 ];
+ var components = m[ 2 ];
+
+ switch ( name ) {
+
+ case 'rgb':
+ case 'rgba':
+
+ if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
+
+ // rgb(255,0,0) rgba(255,0,0,0.5)
+ this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255;
+ this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;
+ this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;
+
+ handleAlpha( color[ 5 ] );
+
+ return this;
+
+ }
+
+ if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
+
+ // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)
+ this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100;
+ this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;
+ this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;
+
+ handleAlpha( color[ 5 ] );
+
+ return this;
+
+ }
+
+ break;
+
+ case 'hsl':
+ case 'hsla':
+
+ if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
+
+ // hsl(120,50%,50%) hsla(120,50%,50%,0.5)
+ var h = parseFloat( color[ 1 ] ) / 360;
+ var s = parseInt( color[ 2 ], 10 ) / 100;
+ var l = parseInt( color[ 3 ], 10 ) / 100;
+
+ handleAlpha( color[ 5 ] );
+
+ return this.setHSL( h, s, l );
+
+ }
+
+ break;
+
+ }
+
+ } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) {
+
+ // hex color
+
+ var hex = m[ 1 ];
+ var size = hex.length;
+
+ if ( size === 3 ) {
+
+ // #ff0
+ this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255;
+ this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255;
+ this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255;
+
+ return this;
+
+ } else if ( size === 6 ) {
+
+ // #ff0000
+ this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255;
+ this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255;
+ this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255;
+
+ return this;
+
+ }
+
+ }
+
+ if ( style && style.length > 0 ) {
+
+ // color keywords
+ var hex = THREE.ColorKeywords[ style ];
+
+ if ( hex !== undefined ) {
+
+ // red
+ this.setHex( hex );
+
+ } else {
+
+ // unknown color
+ console.warn( 'THREE.Color: Unknown color ' + style );
+
+ }
+
+ }
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this.r, this.g, this.b );
+
+ },
+
+ copy: function ( color ) {
+
+ this.r = color.r;
+ this.g = color.g;
+ this.b = color.b;
+
+ return this;
+
+ },
+
+ copyGammaToLinear: function ( color, gammaFactor ) {
+
+ if ( gammaFactor === undefined ) gammaFactor = 2.0;
+
+ this.r = Math.pow( color.r, gammaFactor );
+ this.g = Math.pow( color.g, gammaFactor );
+ this.b = Math.pow( color.b, gammaFactor );
+
+ return this;
+
+ },
+
+ copyLinearToGamma: function ( color, gammaFactor ) {
+
+ if ( gammaFactor === undefined ) gammaFactor = 2.0;
+
+ var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0;
+
+ this.r = Math.pow( color.r, safeInverse );
+ this.g = Math.pow( color.g, safeInverse );
+ this.b = Math.pow( color.b, safeInverse );
+
+ return this;
+
+ },
+
+ convertGammaToLinear: function () {
+
+ var r = this.r, g = this.g, b = this.b;
+
+ this.r = r * r;
+ this.g = g * g;
+ this.b = b * b;
+
+ return this;
+
+ },
+
+ convertLinearToGamma: function () {
+
+ this.r = Math.sqrt( this.r );
+ this.g = Math.sqrt( this.g );
+ this.b = Math.sqrt( this.b );
+
+ return this;
+
+ },
+
+ getHex: function () {
+
+ return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;
+
+ },
+
+ getHexString: function () {
+
+ return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );
+
+ },
+
+ getHSL: function ( optionalTarget ) {
+
+ // h,s,l ranges are in 0.0 - 1.0
+
+ var hsl = optionalTarget || { h: 0, s: 0, l: 0 };
+
+ var r = this.r, g = this.g, b = this.b;
+
+ var max = Math.max( r, g, b );
+ var min = Math.min( r, g, b );
+
+ var hue, saturation;
+ var lightness = ( min + max ) / 2.0;
+
+ if ( min === max ) {
+
+ hue = 0;
+ saturation = 0;
+
+ } else {
+
+ var delta = max - min;
+
+ saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );
+
+ switch ( max ) {
+
+ case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;
+ case g: hue = ( b - r ) / delta + 2; break;
+ case b: hue = ( r - g ) / delta + 4; break;
+
+ }
+
+ hue /= 6;
+
+ }
+
+ hsl.h = hue;
+ hsl.s = saturation;
+ hsl.l = lightness;
+
+ return hsl;
+
+ },
+
+ getStyle: function () {
+
+ return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';
+
+ },
+
+ offsetHSL: function ( h, s, l ) {
+
+ var hsl = this.getHSL();
+
+ hsl.h += h; hsl.s += s; hsl.l += l;
+
+ this.setHSL( hsl.h, hsl.s, hsl.l );
+
+ return this;
+
+ },
+
+ add: function ( color ) {
+
+ this.r += color.r;
+ this.g += color.g;
+ this.b += color.b;
+
+ return this;
+
+ },
+
+ addColors: function ( color1, color2 ) {
+
+ this.r = color1.r + color2.r;
+ this.g = color1.g + color2.g;
+ this.b = color1.b + color2.b;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.r += s;
+ this.g += s;
+ this.b += s;
+
+ return this;
+
+ },
+
+ multiply: function ( color ) {
+
+ this.r *= color.r;
+ this.g *= color.g;
+ this.b *= color.b;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( s ) {
+
+ this.r *= s;
+ this.g *= s;
+ this.b *= s;
+
+ return this;
+
+ },
+
+ lerp: function ( color, alpha ) {
+
+ this.r += ( color.r - this.r ) * alpha;
+ this.g += ( color.g - this.g ) * alpha;
+ this.b += ( color.b - this.b ) * alpha;
+
+ return this;
+
+ },
+
+ equals: function ( c ) {
+
+ return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.r = array[ offset ];
+ this.g = array[ offset + 1 ];
+ this.b = array[ offset + 2 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.r;
+ array[ offset + 1 ] = this.g;
+ array[ offset + 2 ] = this.b;
+
+ return array;
+
+ }
+
+};
+
+THREE.ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,
+'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,
+'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,
+'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,
+'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,
+'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,
+'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,
+'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,
+'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,
+'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,
+'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,
+'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,
+'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,
+'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,
+'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,
+'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,
+'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,
+'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,
+'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,
+'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,
+'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,
+'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,
+'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,
+'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };
+
+// File:src/math/Quaternion.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Quaternion = function ( x, y, z, w ) {
+
+ this._x = x || 0;
+ this._y = y || 0;
+ this._z = z || 0;
+ this._w = ( w !== undefined ) ? w : 1;
+
+};
+
+THREE.Quaternion.prototype = {
+
+ constructor: THREE.Quaternion,
+
+ get x () {
+
+ return this._x;
+
+ },
+
+ set x ( value ) {
+
+ this._x = value;
+ this.onChangeCallback();
+
+ },
+
+ get y () {
+
+ return this._y;
+
+ },
+
+ set y ( value ) {
+
+ this._y = value;
+ this.onChangeCallback();
+
+ },
+
+ get z () {
+
+ return this._z;
+
+ },
+
+ set z ( value ) {
+
+ this._z = value;
+ this.onChangeCallback();
+
+ },
+
+ get w () {
+
+ return this._w;
+
+ },
+
+ set w ( value ) {
+
+ this._w = value;
+ this.onChangeCallback();
+
+ },
+
+ set: function ( x, y, z, w ) {
+
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._w = w;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this._x, this._y, this._z, this._w );
+
+ },
+
+ copy: function ( quaternion ) {
+
+ this._x = quaternion.x;
+ this._y = quaternion.y;
+ this._z = quaternion.z;
+ this._w = quaternion.w;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromEuler: function ( euler, update ) {
+
+ if ( euler instanceof THREE.Euler === false ) {
+
+ throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ // http://www.mathworks.com/matlabcentral/fileexchange/
+ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
+ // content/SpinCalc.m
+
+ var c1 = Math.cos( euler._x / 2 );
+ var c2 = Math.cos( euler._y / 2 );
+ var c3 = Math.cos( euler._z / 2 );
+ var s1 = Math.sin( euler._x / 2 );
+ var s2 = Math.sin( euler._y / 2 );
+ var s3 = Math.sin( euler._z / 2 );
+
+ var order = euler.order;
+
+ if ( order === 'XYZ' ) {
+
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if ( order === 'YXZ' ) {
+
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+
+ } else if ( order === 'ZXY' ) {
+
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if ( order === 'ZYX' ) {
+
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+
+ } else if ( order === 'YZX' ) {
+
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if ( order === 'XZY' ) {
+
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+
+ }
+
+ if ( update !== false ) this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromAxisAngle: function ( axis, angle ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
+
+ // assumes axis is normalized
+
+ var halfAngle = angle / 2, s = Math.sin( halfAngle );
+
+ this._x = axis.x * s;
+ this._y = axis.y * s;
+ this._z = axis.z * s;
+ this._w = Math.cos( halfAngle );
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromRotationMatrix: function ( m ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ var te = m.elements,
+
+ m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+ m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+ m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
+
+ trace = m11 + m22 + m33,
+ s;
+
+ if ( trace > 0 ) {
+
+ s = 0.5 / Math.sqrt( trace + 1.0 );
+
+ this._w = 0.25 / s;
+ this._x = ( m32 - m23 ) * s;
+ this._y = ( m13 - m31 ) * s;
+ this._z = ( m21 - m12 ) * s;
+
+ } else if ( m11 > m22 && m11 > m33 ) {
+
+ s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
+
+ this._w = ( m32 - m23 ) / s;
+ this._x = 0.25 * s;
+ this._y = ( m12 + m21 ) / s;
+ this._z = ( m13 + m31 ) / s;
+
+ } else if ( m22 > m33 ) {
+
+ s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
+
+ this._w = ( m13 - m31 ) / s;
+ this._x = ( m12 + m21 ) / s;
+ this._y = 0.25 * s;
+ this._z = ( m23 + m32 ) / s;
+
+ } else {
+
+ s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
+
+ this._w = ( m21 - m12 ) / s;
+ this._x = ( m13 + m31 ) / s;
+ this._y = ( m23 + m32 ) / s;
+ this._z = 0.25 * s;
+
+ }
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromUnitVectors: function () {
+
+ // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
+
+ // assumes direction vectors vFrom and vTo are normalized
+
+ var v1, r;
+
+ var EPS = 0.000001;
+
+ return function ( vFrom, vTo ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ r = vFrom.dot( vTo ) + 1;
+
+ if ( r < EPS ) {
+
+ r = 0;
+
+ if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
+
+ v1.set( - vFrom.y, vFrom.x, 0 );
+
+ } else {
+
+ v1.set( 0, - vFrom.z, vFrom.y );
+
+ }
+
+ } else {
+
+ v1.crossVectors( vFrom, vTo );
+
+ }
+
+ this._x = v1.x;
+ this._y = v1.y;
+ this._z = v1.z;
+ this._w = r;
+
+ this.normalize();
+
+ return this;
+
+ }
+
+ }(),
+
+ inverse: function () {
+
+ this.conjugate().normalize();
+
+ return this;
+
+ },
+
+ conjugate: function () {
+
+ this._x *= - 1;
+ this._y *= - 1;
+ this._z *= - 1;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
+
+ },
+
+ lengthSq: function () {
+
+ return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
+
+ },
+
+ normalize: function () {
+
+ var l = this.length();
+
+ if ( l === 0 ) {
+
+ this._x = 0;
+ this._y = 0;
+ this._z = 0;
+ this._w = 1;
+
+ } else {
+
+ l = 1 / l;
+
+ this._x = this._x * l;
+ this._y = this._y * l;
+ this._z = this._z * l;
+ this._w = this._w * l;
+
+ }
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ multiply: function ( q, p ) {
+
+ if ( p !== undefined ) {
+
+ console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
+ return this.multiplyQuaternions( q, p );
+
+ }
+
+ return this.multiplyQuaternions( this, q );
+
+ },
+
+ multiplyQuaternions: function ( a, b ) {
+
+ // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
+
+ var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
+ var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
+
+ this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
+ this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
+ this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
+ this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ multiplyVector3: function ( vector ) {
+
+ console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );
+ return vector.applyQuaternion( this );
+
+ },
+
+ slerp: function ( qb, t ) {
+
+ if ( t === 0 ) return this;
+ if ( t === 1 ) return this.copy( qb );
+
+ var x = this._x, y = this._y, z = this._z, w = this._w;
+
+ // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
+
+ var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
+
+ if ( cosHalfTheta < 0 ) {
+
+ this._w = - qb._w;
+ this._x = - qb._x;
+ this._y = - qb._y;
+ this._z = - qb._z;
+
+ cosHalfTheta = - cosHalfTheta;
+
+ } else {
+
+ this.copy( qb );
+
+ }
+
+ if ( cosHalfTheta >= 1.0 ) {
+
+ this._w = w;
+ this._x = x;
+ this._y = y;
+ this._z = z;
+
+ return this;
+
+ }
+
+ var halfTheta = Math.acos( cosHalfTheta );
+ var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
+
+ if ( Math.abs( sinHalfTheta ) < 0.001 ) {
+
+ this._w = 0.5 * ( w + this._w );
+ this._x = 0.5 * ( x + this._x );
+ this._y = 0.5 * ( y + this._y );
+ this._z = 0.5 * ( z + this._z );
+
+ return this;
+
+ }
+
+ var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
+ ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
+
+ this._w = ( w * ratioA + this._w * ratioB );
+ this._x = ( x * ratioA + this._x * ratioB );
+ this._y = ( y * ratioA + this._y * ratioB );
+ this._z = ( z * ratioA + this._z * ratioB );
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ equals: function ( quaternion ) {
+
+ return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this._x = array[ offset ];
+ this._y = array[ offset + 1 ];
+ this._z = array[ offset + 2 ];
+ this._w = array[ offset + 3 ];
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this._x;
+ array[ offset + 1 ] = this._y;
+ array[ offset + 2 ] = this._z;
+ array[ offset + 3 ] = this._w;
+
+ return array;
+
+ },
+
+ onChange: function ( callback ) {
+
+ this.onChangeCallback = callback;
+
+ return this;
+
+ },
+
+ onChangeCallback: function () {}
+
+};
+
+THREE.Quaternion.slerp = function ( qa, qb, qm, t ) {
+
+ return qm.copy( qa ).slerp( qb, t );
+
+};
+
+// File:src/math/Vector2.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author philogb / http://blog.thejit.org/
+ * @author egraether / http://egraether.com/
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ */
+
+THREE.Vector2 = function ( x, y ) {
+
+ this.x = x || 0;
+ this.y = y || 0;
+
+};
+
+THREE.Vector2.prototype = {
+
+ constructor: THREE.Vector2,
+
+ get width() { return this.x },
+ set width( value ) { this.x = value },
+
+ get height() { return this.y },
+ set height( value ) { this.y = value },
+
+ //
+
+ set: function ( x, y ) {
+
+ this.x = x;
+ this.y = y;
+
+ return this;
+
+ },
+
+ setX: function ( x ) {
+
+ this.x = x;
+
+ return this;
+
+ },
+
+ setY: function ( y ) {
+
+ this.y = y;
+
+ return this;
+
+ },
+
+ setComponent: function ( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ getComponent: function ( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this.x, this.y );
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+
+ return this;
+
+ },
+
+ addScaledVector: function ( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+
+ return this;
+
+ },
+
+ subScalar: function ( s ) {
+
+ this.x -= s;
+ this.y -= s;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+
+ return this;
+
+ },
+
+ multiply: function ( v ) {
+
+ this.x *= v.x;
+ this.y *= v.y;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( scalar ) {
+
+ if ( isFinite( scalar ) ) {
+ this.x *= scalar;
+ this.y *= scalar;
+ } else {
+ this.x = 0;
+ this.y = 0;
+ }
+
+ return this;
+
+ },
+
+ divide: function ( v ) {
+
+ this.x /= v.x;
+ this.y /= v.y;
+
+ return this;
+
+ },
+
+ divideScalar: function ( scalar ) {
+
+ return this.multiplyScalar( 1 / scalar );
+
+ },
+
+ min: function ( v ) {
+
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // This function assumes min < max, if this assumption isn't true it will not operate correctly
+
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+
+ return this;
+
+ },
+
+ clampScalar: function () {
+
+ var min, max;
+
+ return function clampScalar( minVal, maxVal ) {
+
+ if ( min === undefined ) {
+
+ min = new THREE.Vector2();
+ max = new THREE.Vector2();
+
+ }
+
+ min.set( minVal, minVal );
+ max.set( maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ }(),
+
+ clampLength: function ( min, max ) {
+
+ var length = this.length();
+
+ this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length );
+
+ return this;
+
+ },
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y;
+
+ },
+
+ lengthSq: function () {
+
+ return this.x * this.x + this.y * this.y;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this.x * this.x + this.y * this.y );
+
+ },
+
+ lengthManhattan: function() {
+
+ return Math.abs( this.x ) + Math.abs( this.y );
+
+ },
+
+ normalize: function () {
+
+ return this.divideScalar( this.length() );
+
+ },
+
+ distanceTo: function ( v ) {
+
+ return Math.sqrt( this.distanceToSquared( v ) );
+
+ },
+
+ distanceToSquared: function ( v ) {
+
+ var dx = this.x - v.x, dy = this.y - v.y;
+ return dx * dx + dy * dy;
+
+ },
+
+ setLength: function ( length ) {
+
+ return this.multiplyScalar( length / this.length() );
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+
+ return this;
+
+ },
+
+ lerpVectors: function ( v1, v2, alpha ) {
+
+ this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+ return this;
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+
+ return array;
+
+ },
+
+ fromAttribute: function ( attribute, index, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ index = index * attribute.itemSize + offset;
+
+ this.x = attribute.array[ index ];
+ this.y = attribute.array[ index + 1 ];
+
+ return this;
+
+ },
+
+ rotateAround: function ( center, angle ) {
+
+ var c = Math.cos( angle ), s = Math.sin( angle );
+
+ var x = this.x - center.x;
+ var y = this.y - center.y;
+
+ this.x = x * c - y * s + center.x;
+ this.y = x * s + y * c + center.y;
+
+ return this;
+
+ }
+
+};
+
+// File:src/math/Vector3.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author *kile / http://kile.stravaganza.org/
+ * @author philogb / http://blog.thejit.org/
+ * @author mikael emtinger / http://gomo.se/
+ * @author egraether / http://egraether.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.Vector3 = function ( x, y, z ) {
+
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+
+};
+
+THREE.Vector3.prototype = {
+
+ constructor: THREE.Vector3,
+
+ set: function ( x, y, z ) {
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+
+ return this;
+
+ },
+
+ setX: function ( x ) {
+
+ this.x = x;
+
+ return this;
+
+ },
+
+ setY: function ( y ) {
+
+ this.y = y;
+
+ return this;
+
+ },
+
+ setZ: function ( z ) {
+
+ this.z = z;
+
+ return this;
+
+ },
+
+ setComponent: function ( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ case 2: this.z = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ getComponent: function ( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ case 2: return this.z;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this.x, this.y, this.z );
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+ this.z += v.z;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+ this.z += s;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+ this.z = a.z + b.z;
+
+ return this;
+
+ },
+
+ addScaledVector: function ( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+ this.z += v.z * s;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+ this.z -= v.z;
+
+ return this;
+
+ },
+
+ subScalar: function ( s ) {
+
+ this.x -= s;
+ this.y -= s;
+ this.z -= s;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+ this.z = a.z - b.z;
+
+ return this;
+
+ },
+
+ multiply: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
+ return this.multiplyVectors( v, w );
+
+ }
+
+ this.x *= v.x;
+ this.y *= v.y;
+ this.z *= v.z;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( scalar ) {
+
+ if ( isFinite( scalar ) ) {
+ this.x *= scalar;
+ this.y *= scalar;
+ this.z *= scalar;
+ } else {
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ }
+
+ return this;
+
+ },
+
+ multiplyVectors: function ( a, b ) {
+
+ this.x = a.x * b.x;
+ this.y = a.y * b.y;
+ this.z = a.z * b.z;
+
+ return this;
+
+ },
+
+ applyEuler: function () {
+
+ var quaternion;
+
+ return function applyEuler( euler ) {
+
+ if ( euler instanceof THREE.Euler === false ) {
+
+ console.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
+
+ this.applyQuaternion( quaternion.setFromEuler( euler ) );
+
+ return this;
+
+ };
+
+ }(),
+
+ applyAxisAngle: function () {
+
+ var quaternion;
+
+ return function applyAxisAngle( axis, angle ) {
+
+ if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
+
+ this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
+
+ return this;
+
+ };
+
+ }(),
+
+ applyMatrix3: function ( m ) {
+
+ var x = this.x;
+ var y = this.y;
+ var z = this.z;
+
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
+ this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
+ this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
+
+ return this;
+
+ },
+
+ applyMatrix4: function ( m ) {
+
+ // input: THREE.Matrix4 affine matrix
+
+ var x = this.x, y = this.y, z = this.z;
+
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ];
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ];
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ];
+
+ return this;
+
+ },
+
+ applyProjection: function ( m ) {
+
+ // input: THREE.Matrix4 projection matrix
+
+ var x = this.x, y = this.y, z = this.z;
+
+ var e = m.elements;
+ var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide
+
+ this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d;
+ this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d;
+ this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d;
+
+ return this;
+
+ },
+
+ applyQuaternion: function ( q ) {
+
+ var x = this.x;
+ var y = this.y;
+ var z = this.z;
+
+ var qx = q.x;
+ var qy = q.y;
+ var qz = q.z;
+ var qw = q.w;
+
+ // calculate quat * vector
+
+ var ix = qw * x + qy * z - qz * y;
+ var iy = qw * y + qz * x - qx * z;
+ var iz = qw * z + qx * y - qy * x;
+ var iw = - qx * x - qy * y - qz * z;
+
+ // calculate result * inverse quat
+
+ this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
+ this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
+ this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
+
+ return this;
+
+ },
+
+ project: function () {
+
+ var matrix;
+
+ return function project( camera ) {
+
+ if ( matrix === undefined ) matrix = new THREE.Matrix4();
+
+ matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
+ return this.applyProjection( matrix );
+
+ };
+
+ }(),
+
+ unproject: function () {
+
+ var matrix;
+
+ return function unproject( camera ) {
+
+ if ( matrix === undefined ) matrix = new THREE.Matrix4();
+
+ matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
+ return this.applyProjection( matrix );
+
+ };
+
+ }(),
+
+ transformDirection: function ( m ) {
+
+ // input: THREE.Matrix4 affine matrix
+ // vector interpreted as a direction
+
+ var x = this.x, y = this.y, z = this.z;
+
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
+
+ this.normalize();
+
+ return this;
+
+ },
+
+ divide: function ( v ) {
+
+ this.x /= v.x;
+ this.y /= v.y;
+ this.z /= v.z;
+
+ return this;
+
+ },
+
+ divideScalar: function ( scalar ) {
+
+ return this.multiplyScalar( 1 / scalar );
+
+ },
+
+ min: function ( v ) {
+
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+ this.z = Math.min( this.z, v.z );
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+ this.z = Math.max( this.z, v.z );
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // This function assumes min < max, if this assumption isn't true it will not operate correctly
+
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+ this.z = Math.max( min.z, Math.min( max.z, this.z ) );
+
+ return this;
+
+ },
+
+ clampScalar: function () {
+
+ var min, max;
+
+ return function clampScalar( minVal, maxVal ) {
+
+ if ( min === undefined ) {
+
+ min = new THREE.Vector3();
+ max = new THREE.Vector3();
+
+ }
+
+ min.set( minVal, minVal, minVal );
+ max.set( maxVal, maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ }(),
+
+ clampLength: function ( min, max ) {
+
+ var length = this.length();
+
+ this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length );
+
+ return this;
+
+ },
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+ this.z = Math.floor( this.z );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+ this.z = Math.ceil( this.z );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+ this.z = Math.round( this.z );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+ this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+ this.z = - this.z;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y + this.z * v.z;
+
+ },
+
+ 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 );
+
+ },
+
+ lengthManhattan: function () {
+
+ return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
+
+ },
+
+ normalize: function () {
+
+ return this.divideScalar( this.length() );
+
+ },
+
+ setLength: function ( length ) {
+
+ return this.multiplyScalar( length / this.length() );
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+ this.z += ( v.z - this.z ) * alpha;
+
+ return this;
+
+ },
+
+ lerpVectors: function ( v1, v2, alpha ) {
+
+ this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+ return this;
+
+ },
+
+ cross: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
+ return this.crossVectors( v, w );
+
+ }
+
+ var x = this.x, y = this.y, z = this.z;
+
+ this.x = y * v.z - z * v.y;
+ this.y = z * v.x - x * v.z;
+ this.z = x * v.y - y * v.x;
+
+ return this;
+
+ },
+
+ crossVectors: function ( a, b ) {
+
+ var ax = a.x, ay = a.y, az = a.z;
+ var bx = b.x, by = b.y, bz = b.z;
+
+ this.x = ay * bz - az * by;
+ this.y = az * bx - ax * bz;
+ this.z = ax * by - ay * bx;
+
+ return this;
+
+ },
+
+ projectOnVector: function () {
+
+ var v1, dot;
+
+ return function projectOnVector( vector ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ v1.copy( vector ).normalize();
+
+ dot = this.dot( v1 );
+
+ return this.copy( v1 ).multiplyScalar( dot );
+
+ };
+
+ }(),
+
+ projectOnPlane: function () {
+
+ var v1;
+
+ return function projectOnPlane( planeNormal ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ v1.copy( this ).projectOnVector( planeNormal );
+
+ return this.sub( v1 );
+
+ }
+
+ }(),
+
+ reflect: function () {
+
+ // reflect incident vector off plane orthogonal to normal
+ // normal is assumed to have unit length
+
+ var v1;
+
+ return function reflect( normal ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
+
+ }
+
+ }(),
+
+ angleTo: function ( v ) {
+
+ var theta = this.dot( v ) / ( this.length() * v.length() );
+
+ // clamp, to handle numerical problems
+
+ return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) );
+
+ },
+
+ distanceTo: function ( v ) {
+
+ return Math.sqrt( this.distanceToSquared( v ) );
+
+ },
+
+ distanceToSquared: function ( v ) {
+
+ var dx = this.x - v.x;
+ var dy = this.y - v.y;
+ var dz = this.z - v.z;
+
+ return dx * dx + dy * dy + dz * dz;
+
+ },
+
+ setEulerFromRotationMatrix: function ( m, order ) {
+
+ console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' );
+
+ },
+
+ setEulerFromQuaternion: function ( q, order ) {
+
+ console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' );
+
+ },
+
+ getPositionFromMatrix: function ( m ) {
+
+ console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' );
+
+ return this.setFromMatrixPosition( m );
+
+ },
+
+ getScaleFromMatrix: function ( m ) {
+
+ console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' );
+
+ return this.setFromMatrixScale( m );
+
+ },
+
+ getColumnFromMatrix: function ( index, matrix ) {
+
+ console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' );
+
+ return this.setFromMatrixColumn( index, matrix );
+
+ },
+
+ setFromMatrixPosition: function ( m ) {
+
+ this.x = m.elements[ 12 ];
+ this.y = m.elements[ 13 ];
+ this.z = m.elements[ 14 ];
+
+ return this;
+
+ },
+
+ setFromMatrixScale: function ( m ) {
+
+ var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length();
+ var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length();
+ var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length();
+
+ this.x = sx;
+ this.y = sy;
+ this.z = sz;
+
+ return this;
+
+ },
+
+ setFromMatrixColumn: function ( index, matrix ) {
+
+ var offset = index * 4;
+
+ var me = matrix.elements;
+
+ this.x = me[ offset ];
+ this.y = me[ offset + 1 ];
+ this.z = me[ offset + 2 ];
+
+ return this;
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+ this.z = array[ offset + 2 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+ array[ offset + 2 ] = this.z;
+
+ return array;
+
+ },
+
+ fromAttribute: function ( attribute, index, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ index = index * attribute.itemSize + offset;
+
+ this.x = attribute.array[ index ];
+ this.y = attribute.array[ index + 1 ];
+ this.z = attribute.array[ index + 2 ];
+
+ return this;
+
+ }
+
+};
+
+// File:src/math/Vector4.js
+
+/**
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author philogb / http://blog.thejit.org/
+ * @author mikael emtinger / http://gomo.se/
+ * @author egraether / http://egraether.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.Vector4 = function ( x, y, z, w ) {
+
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+ this.w = ( w !== undefined ) ? w : 1;
+
+};
+
+THREE.Vector4.prototype = {
+
+ constructor: THREE.Vector4,
+
+ set: function ( x, y, z, w ) {
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+
+ return this;
+
+ },
+
+ setX: function ( x ) {
+
+ this.x = x;
+
+ return this;
+
+ },
+
+ setY: function ( y ) {
+
+ this.y = y;
+
+ return this;
+
+ },
+
+ setZ: function ( z ) {
+
+ this.z = z;
+
+ return this;
+
+ },
+
+ setW: function ( w ) {
+
+ this.w = w;
+
+ return this;
+
+ },
+
+ setComponent: function ( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ case 2: this.z = value; break;
+ case 3: this.w = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ getComponent: function ( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ case 2: return this.z;
+ case 3: return this.w;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this.x, this.y, this.z, this.w );
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+ this.w = ( v.w !== undefined ) ? v.w : 1;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+ this.z += v.z;
+ this.w += v.w;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+ this.z += s;
+ this.w += s;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+ this.z = a.z + b.z;
+ this.w = a.w + b.w;
+
+ return this;
+
+ },
+
+ addScaledVector: function ( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+ this.z += v.z * s;
+ this.w += v.w * s;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+ this.z -= v.z;
+ this.w -= v.w;
+
+ return this;
+
+ },
+
+ subScalar: function ( s ) {
+
+ this.x -= s;
+ this.y -= s;
+ this.z -= s;
+ this.w -= s;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+ this.z = a.z - b.z;
+ this.w = a.w - b.w;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( scalar ) {
+
+ if ( isFinite( scalar ) ) {
+ this.x *= scalar;
+ this.y *= scalar;
+ this.z *= scalar;
+ this.w *= scalar;
+ } else {
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ this.w = 0;
+ }
+
+ return this;
+
+ },
+
+ applyMatrix4: function ( m ) {
+
+ var x = this.x;
+ var y = this.y;
+ var z = this.z;
+ var w = this.w;
+
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;
+ this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;
+
+ return this;
+
+ },
+
+ divideScalar: function ( scalar ) {
+
+ return this.multiplyScalar( 1 / scalar );
+
+ },
+
+ setAxisAngleFromQuaternion: function ( q ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
+
+ // q is assumed to be normalized
+
+ this.w = 2 * Math.acos( q.w );
+
+ var s = Math.sqrt( 1 - q.w * q.w );
+
+ if ( s < 0.0001 ) {
+
+ this.x = 1;
+ this.y = 0;
+ this.z = 0;
+
+ } else {
+
+ this.x = q.x / s;
+ this.y = q.y / s;
+ this.z = q.z / s;
+
+ }
+
+ return this;
+
+ },
+
+ setAxisAngleFromRotationMatrix: function ( m ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ var angle, x, y, z, // variables for result
+ epsilon = 0.01, // margin to allow for rounding errors
+ epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees
+
+ te = m.elements,
+
+ m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+ m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+ m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
+
+ if ( ( Math.abs( m12 - m21 ) < epsilon )
+ && ( Math.abs( m13 - m31 ) < epsilon )
+ && ( Math.abs( m23 - m32 ) < epsilon ) ) {
+
+ // singularity found
+ // first check for identity matrix which must have +1 for all terms
+ // in leading diagonal and zero in other terms
+
+ if ( ( Math.abs( m12 + m21 ) < epsilon2 )
+ && ( Math.abs( m13 + m31 ) < epsilon2 )
+ && ( Math.abs( m23 + m32 ) < epsilon2 )
+ && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {
+
+ // this singularity is identity matrix so angle = 0
+
+ this.set( 1, 0, 0, 0 );
+
+ return this; // zero angle, arbitrary axis
+
+ }
+
+ // otherwise this singularity is angle = 180
+
+ angle = Math.PI;
+
+ var xx = ( m11 + 1 ) / 2;
+ var yy = ( m22 + 1 ) / 2;
+ var zz = ( m33 + 1 ) / 2;
+ var xy = ( m12 + m21 ) / 4;
+ var xz = ( m13 + m31 ) / 4;
+ var yz = ( m23 + m32 ) / 4;
+
+ if ( ( xx > yy ) && ( xx > zz ) ) {
+
+ // m11 is the largest diagonal term
+
+ if ( xx < epsilon ) {
+
+ x = 0;
+ y = 0.707106781;
+ z = 0.707106781;
+
+ } else {
+
+ x = Math.sqrt( xx );
+ y = xy / x;
+ z = xz / x;
+
+ }
+
+ } else if ( yy > zz ) {
+
+ // m22 is the largest diagonal term
+
+ if ( yy < epsilon ) {
+
+ x = 0.707106781;
+ y = 0;
+ z = 0.707106781;
+
+ } else {
+
+ y = Math.sqrt( yy );
+ x = xy / y;
+ z = yz / y;
+
+ }
+
+ } else {
+
+ // m33 is the largest diagonal term so base result on this
+
+ if ( zz < epsilon ) {
+
+ x = 0.707106781;
+ y = 0.707106781;
+ z = 0;
+
+ } else {
+
+ z = Math.sqrt( zz );
+ x = xz / z;
+ y = yz / z;
+
+ }
+
+ }
+
+ this.set( x, y, z, angle );
+
+ return this; // return 180 deg rotation
+
+ }
+
+ // as we have reached here there are no singularities so we can handle normally
+
+ var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 )
+ + ( m13 - m31 ) * ( m13 - m31 )
+ + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize
+
+ if ( Math.abs( s ) < 0.001 ) s = 1;
+
+ // prevent divide by zero, should not happen if matrix is orthogonal and should be
+ // caught by singularity test above, but I've left it in just in case
+
+ this.x = ( m32 - m23 ) / s;
+ this.y = ( m13 - m31 ) / s;
+ this.z = ( m21 - m12 ) / s;
+ this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );
+
+ return this;
+
+ },
+
+ min: function ( v ) {
+
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+ this.z = Math.min( this.z, v.z );
+ this.w = Math.min( this.w, v.w );
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+ this.z = Math.max( this.z, v.z );
+ this.w = Math.max( this.w, v.w );
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // This function assumes min < max, if this assumption isn't true it will not operate correctly
+
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+ this.z = Math.max( min.z, Math.min( max.z, this.z ) );
+ this.w = Math.max( min.w, Math.min( max.w, this.w ) );
+
+ return this;
+
+ },
+
+ clampScalar: function () {
+
+ var min, max;
+
+ return function clampScalar( minVal, maxVal ) {
+
+ if ( min === undefined ) {
+
+ min = new THREE.Vector4();
+ max = new THREE.Vector4();
+
+ }
+
+ min.set( minVal, minVal, minVal, minVal );
+ max.set( maxVal, maxVal, maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ }(),
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+ this.z = Math.floor( this.z );
+ this.w = Math.floor( this.w );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+ this.z = Math.ceil( this.z );
+ this.w = Math.ceil( this.w );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+ this.z = Math.round( this.z );
+ this.w = Math.round( this.w );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+ this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
+ this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+ this.z = - this.z;
+ this.w = - this.w;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;
+
+ },
+
+ lengthSq: function () {
+
+ return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );
+
+ },
+
+ lengthManhattan: 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() );
+
+ },
+
+ setLength: function ( length ) {
+
+ return this.multiplyScalar( length / this.length() );
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+ this.z += ( v.z - this.z ) * alpha;
+ this.w += ( v.w - this.w ) * alpha;
+
+ return this;
+
+ },
+
+ lerpVectors: function ( v1, v2, alpha ) {
+
+ this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+ return this;
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+ this.z = array[ offset + 2 ];
+ this.w = array[ offset + 3 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+ array[ offset + 2 ] = this.z;
+ array[ offset + 3 ] = this.w;
+
+ return array;
+
+ },
+
+ fromAttribute: function ( attribute, index, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ index = index * attribute.itemSize + offset;
+
+ this.x = attribute.array[ index ];
+ this.y = attribute.array[ index + 1 ];
+ this.z = attribute.array[ index + 2 ];
+ this.w = attribute.array[ index + 3 ];
+
+ return this;
+
+ }
+
+};
+
+// File:src/math/Euler.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Euler = function ( x, y, z, order ) {
+
+ this._x = x || 0;
+ this._y = y || 0;
+ this._z = z || 0;
+ this._order = order || THREE.Euler.DefaultOrder;
+
+};
+
+THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];
+
+THREE.Euler.DefaultOrder = 'XYZ';
+
+THREE.Euler.prototype = {
+
+ constructor: THREE.Euler,
+
+ get x () {
+
+ return this._x;
+
+ },
+
+ set x ( value ) {
+
+ this._x = value;
+ this.onChangeCallback();
+
+ },
+
+ get y () {
+
+ return this._y;
+
+ },
+
+ set y ( value ) {
+
+ this._y = value;
+ this.onChangeCallback();
+
+ },
+
+ get z () {
+
+ return this._z;
+
+ },
+
+ set z ( value ) {
+
+ this._z = value;
+ this.onChangeCallback();
+
+ },
+
+ get order () {
+
+ return this._order;
+
+ },
+
+ set order ( value ) {
+
+ this._order = value;
+ this.onChangeCallback();
+
+ },
+
+ set: function ( x, y, z, order ) {
+
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._order = order || this._order;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this._x, this._y, this._z, this._order);
+
+ },
+
+ copy: function ( euler ) {
+
+ this._x = euler._x;
+ this._y = euler._y;
+ this._z = euler._z;
+ this._order = euler._order;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromRotationMatrix: function ( m, order, update ) {
+
+ var clamp = THREE.Math.clamp;
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ var te = m.elements;
+ var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
+ var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
+ var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
+
+ order = order || this._order;
+
+ if ( order === 'XYZ' ) {
+
+ this._y = Math.asin( clamp( m13, - 1, 1 ) );
+
+ if ( Math.abs( m13 ) < 0.99999 ) {
+
+ this._x = Math.atan2( - m23, m33 );
+ this._z = Math.atan2( - m12, m11 );
+
+ } else {
+
+ this._x = Math.atan2( m32, m22 );
+ this._z = 0;
+
+ }
+
+ } else if ( order === 'YXZ' ) {
+
+ this._x = Math.asin( - clamp( m23, - 1, 1 ) );
+
+ if ( Math.abs( m23 ) < 0.99999 ) {
+
+ this._y = Math.atan2( m13, m33 );
+ this._z = Math.atan2( m21, m22 );
+
+ } else {
+
+ this._y = Math.atan2( - m31, m11 );
+ this._z = 0;
+
+ }
+
+ } else if ( order === 'ZXY' ) {
+
+ this._x = Math.asin( clamp( m32, - 1, 1 ) );
+
+ if ( Math.abs( m32 ) < 0.99999 ) {
+
+ this._y = Math.atan2( - m31, m33 );
+ this._z = Math.atan2( - m12, m22 );
+
+ } else {
+
+ this._y = 0;
+ this._z = Math.atan2( m21, m11 );
+
+ }
+
+ } else if ( order === 'ZYX' ) {
+
+ this._y = Math.asin( - clamp( m31, - 1, 1 ) );
+
+ if ( Math.abs( m31 ) < 0.99999 ) {
+
+ this._x = Math.atan2( m32, m33 );
+ this._z = Math.atan2( m21, m11 );
+
+ } else {
+
+ this._x = 0;
+ this._z = Math.atan2( - m12, m22 );
+
+ }
+
+ } else if ( order === 'YZX' ) {
+
+ this._z = Math.asin( clamp( m21, - 1, 1 ) );
+
+ if ( Math.abs( m21 ) < 0.99999 ) {
+
+ this._x = Math.atan2( - m23, m22 );
+ this._y = Math.atan2( - m31, m11 );
+
+ } else {
+
+ this._x = 0;
+ this._y = Math.atan2( m13, m33 );
+
+ }
+
+ } else if ( order === 'XZY' ) {
+
+ this._z = Math.asin( - clamp( m12, - 1, 1 ) );
+
+ if ( Math.abs( m12 ) < 0.99999 ) {
+
+ this._x = Math.atan2( m32, m22 );
+ this._y = Math.atan2( m13, m11 );
+
+ } else {
+
+ this._x = Math.atan2( - m23, m33 );
+ this._y = 0;
+
+ }
+
+ } else {
+
+ console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order )
+
+ }
+
+ this._order = order;
+
+ if ( update !== false ) this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromQuaternion: function () {
+
+ var matrix;
+
+ return function ( q, order, update ) {
+
+ if ( matrix === undefined ) matrix = new THREE.Matrix4();
+ matrix.makeRotationFromQuaternion( q );
+ this.setFromRotationMatrix( matrix, order, update );
+
+ return this;
+
+ };
+
+ }(),
+
+ setFromVector3: function ( v, order ) {
+
+ return this.set( v.x, v.y, v.z, order || this._order );
+
+ },
+
+ reorder: function () {
+
+ // WARNING: this discards revolution information -bhouston
+
+ var q = new THREE.Quaternion();
+
+ return function ( newOrder ) {
+
+ q.setFromEuler( this );
+ this.setFromQuaternion( q, newOrder );
+
+ };
+
+ }(),
+
+ equals: function ( euler ) {
+
+ return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
+
+ },
+
+ fromArray: function ( array ) {
+
+ this._x = array[ 0 ];
+ this._y = array[ 1 ];
+ this._z = array[ 2 ];
+ if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this._x;
+ array[ offset + 1 ] = this._y;
+ array[ offset + 2 ] = this._z;
+ array[ offset + 3 ] = this._order;
+
+ return array;
+
+ },
+
+ toVector3: function ( optionalResult ) {
+
+ if ( optionalResult ) {
+
+ return optionalResult.set( this._x, this._y, this._z );
+
+ } else {
+
+ return new THREE.Vector3( this._x, this._y, this._z );
+
+ }
+
+ },
+
+ onChange: function ( callback ) {
+
+ this.onChangeCallback = callback;
+
+ return this;
+
+ },
+
+ onChangeCallback: function () {}
+
+};
+
+// File:src/math/Line3.js
+
+/**
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Line3 = function ( start, end ) {
+
+ this.start = ( start !== undefined ) ? start : new THREE.Vector3();
+ this.end = ( end !== undefined ) ? end : new THREE.Vector3();
+
+};
+
+THREE.Line3.prototype = {
+
+ constructor: THREE.Line3,
+
+ set: function ( start, end ) {
+
+ this.start.copy( start );
+ this.end.copy( end );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( line ) {
+
+ this.start.copy( line.start );
+ this.end.copy( line.end );
+
+ return this;
+
+ },
+
+ center: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 );
+
+ },
+
+ delta: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.subVectors( this.end, this.start );
+
+ },
+
+ distanceSq: function () {
+
+ return this.start.distanceToSquared( this.end );
+
+ },
+
+ distance: function () {
+
+ return this.start.distanceTo( this.end );
+
+ },
+
+ at: function ( t, optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ return this.delta( result ).multiplyScalar( t ).add( this.start );
+
+ },
+
+ closestPointToPointParameter: function () {
+
+ var startP = new THREE.Vector3();
+ var startEnd = new THREE.Vector3();
+
+ return function ( point, clampToLine ) {
+
+ startP.subVectors( point, this.start );
+ startEnd.subVectors( this.end, this.start );
+
+ var startEnd2 = startEnd.dot( startEnd );
+ var startEnd_startP = startEnd.dot( startP );
+
+ var t = startEnd_startP / startEnd2;
+
+ if ( clampToLine ) {
+
+ t = THREE.Math.clamp( t, 0, 1 );
+
+ }
+
+ return t;
+
+ };
+
+ }(),
+
+ closestPointToPoint: function ( point, clampToLine, optionalTarget ) {
+
+ var t = this.closestPointToPointParameter( point, clampToLine );
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ return this.delta( result ).multiplyScalar( t ).add( this.start );
+
+ },
+
+ applyMatrix4: function ( matrix ) {
+
+ this.start.applyMatrix4( matrix );
+ this.end.applyMatrix4( matrix );
+
+ return this;
+
+ },
+
+ equals: function ( line ) {
+
+ return line.start.equals( this.start ) && line.end.equals( this.end );
+
+ }
+
+};
+
+// File:src/math/Box2.js
+
+/**
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Box2 = function ( min, max ) {
+
+ this.min = ( min !== undefined ) ? min : new THREE.Vector2( Infinity, Infinity );
+ this.max = ( max !== undefined ) ? max : new THREE.Vector2( - Infinity, - Infinity );
+
+};
+
+THREE.Box2.prototype = {
+
+ constructor: THREE.Box2,
+
+ set: function ( min, max ) {
+
+ this.min.copy( min );
+ this.max.copy( max );
+
+ return this;
+
+ },
+
+ setFromPoints: function ( points ) {
+
+ this.makeEmpty();
+
+ for ( var i = 0, il = points.length; i < il; i ++ ) {
+
+ this.expandByPoint( points[ i ] )
+
+ }
+
+ return this;
+
+ },
+
+ setFromCenterAndSize: function () {
+
+ var v1 = new THREE.Vector2();
+
+ return function ( center, size ) {
+
+ var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
+ this.min.copy( center ).sub( halfSize );
+ this.max.copy( center ).add( halfSize );
+
+ return this;
+
+ };
+
+ }(),
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( box ) {
+
+ this.min.copy( box.min );
+ this.max.copy( box.max );
+
+ return this;
+
+ },
+
+ makeEmpty: function () {
+
+ this.min.x = this.min.y = Infinity;
+ this.max.x = this.max.y = - Infinity;
+
+ return this;
+
+ },
+
+ empty: function () {
+
+ // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
+
+ return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y );
+
+ },
+
+ center: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector2();
+ return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
+
+ },
+
+ size: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector2();
+ return result.subVectors( this.max, this.min );
+
+ },
+
+ expandByPoint: function ( point ) {
+
+ this.min.min( point );
+ this.max.max( point );
+
+ return this;
+
+ },
+
+ expandByVector: function ( vector ) {
+
+ this.min.sub( vector );
+ this.max.add( vector );
+
+ return this;
+
+ },
+
+ expandByScalar: function ( scalar ) {
+
+ this.min.addScalar( - scalar );
+ this.max.addScalar( scalar );
+
+ return this;
+
+ },
+
+ containsPoint: function ( point ) {
+
+ if ( point.x < this.min.x || point.x > this.max.x ||
+ point.y < this.min.y || point.y > this.max.y ) {
+
+ return false;
+
+ }
+
+ return true;
+
+ },
+
+ containsBox: function ( box ) {
+
+ if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&
+ ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) ) {
+
+ return true;
+
+ }
+
+ return false;
+
+ },
+
+ getParameter: function ( point, optionalTarget ) {
+
+ // This can potentially have a divide by zero if the box
+ // has a size dimension of 0.
+
+ var result = optionalTarget || new THREE.Vector2();
+
+ return result.set(
+ ( point.x - this.min.x ) / ( this.max.x - this.min.x ),
+ ( point.y - this.min.y ) / ( this.max.y - this.min.y )
+ );
+
+ },
+
+ isIntersectionBox: function ( box ) {
+
+ // using 6 splitting planes to rule out intersections.
+
+ if ( box.max.x < this.min.x || box.min.x > this.max.x ||
+ box.max.y < this.min.y || box.min.y > this.max.y ) {
+
+ return false;
+
+ }
+
+ return true;
+
+ },
+
+ clampPoint: function ( point, optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector2();
+ return result.copy( point ).clamp( this.min, this.max );
+
+ },
+
+ distanceToPoint: function () {
+
+ var v1 = new THREE.Vector2();
+
+ return function ( point ) {
+
+ var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
+ return clampedPoint.sub( point ).length();
+
+ };
+
+ }(),
+
+ intersect: function ( box ) {
+
+ this.min.max( box.min );
+ this.max.min( box.max );
+
+ return this;
+
+ },
+
+ union: function ( box ) {
+
+ this.min.min( box.min );
+ this.max.max( box.max );
+
+ return this;
+
+ },
+
+ translate: function ( offset ) {
+
+ this.min.add( offset );
+ this.max.add( offset );
+
+ return this;
+
+ },
+
+ equals: function ( box ) {
+
+ return box.min.equals( this.min ) && box.max.equals( this.max );
+
+ }
+
+};
+
+// File:src/math/Box3.js
+
+/**
+ * @author bhouston / http://clara.io
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.Box3 = function ( min, max ) {
+
+ this.min = ( min !== undefined ) ? min : new THREE.Vector3( Infinity, Infinity, Infinity );
+ this.max = ( max !== undefined ) ? max : new THREE.Vector3( - Infinity, - Infinity, - Infinity );
+
+};
+
+THREE.Box3.prototype = {
+
+ constructor: THREE.Box3,
+
+ set: function ( min, max ) {
+
+ this.min.copy( min );
+ this.max.copy( max );
+
+ return this;
+
+ },
+
+ setFromPoints: function ( points ) {
+
+ this.makeEmpty();
+
+ for ( var i = 0, il = points.length; i < il; i ++ ) {
+
+ this.expandByPoint( points[ i ] );
+
+ }
+
+ return this;
+
+ },
+
+ setFromCenterAndSize: function () {
+
+ var v1 = new THREE.Vector3();
+
+ return function ( center, size ) {
+
+ var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
+
+ this.min.copy( center ).sub( halfSize );
+ this.max.copy( center ).add( halfSize );
+
+ return this;
+
+ };
+
+ }(),
+
+ setFromObject: function () {
+
+ // Computes the world-axis-aligned bounding box of an object (including its children),
+ // accounting for both the object's, and children's, world transforms
+
+ var v1 = new THREE.Vector3();
+
+ return function ( object ) {
+
+ var scope = this;
+
+ object.updateMatrixWorld( true );
+
+ this.makeEmpty();
+
+ object.traverse( function ( node ) {
+
+ var geometry = node.geometry;
+
+ if ( geometry !== undefined ) {
+
+ if ( geometry instanceof THREE.Geometry ) {
+
+ var vertices = geometry.vertices;
+
+ for ( var i = 0, il = vertices.length; i < il; i ++ ) {
+
+ v1.copy( vertices[ i ] );
+
+ v1.applyMatrix4( node.matrixWorld );
+
+ scope.expandByPoint( v1 );
+
+ }
+
+ } else if ( geometry instanceof THREE.BufferGeometry && geometry.attributes[ 'position' ] !== undefined ) {
+
+ var positions = geometry.attributes[ 'position' ].array;
+
+ for ( var i = 0, il = positions.length; i < il; i += 3 ) {
+
+ v1.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
+
+ v1.applyMatrix4( node.matrixWorld );
+
+ scope.expandByPoint( v1 );
+
+ }
+
+ }
+
+ }
+
+ } );
+
+ return this;
+
+ };
+
+ }(),
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( box ) {
+
+ this.min.copy( box.min );
+ this.max.copy( box.max );
+
+ return this;
+
+ },
+
+ makeEmpty: function () {
+
+ this.min.x = this.min.y = this.min.z = Infinity;
+ this.max.x = this.max.y = this.max.z = - Infinity;
+
+ return this;
+
+ },
+
+ empty: function () {
+
+ // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
+
+ return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );
+
+ },
+
+ center: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
+
+ },
+
+ size: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.subVectors( this.max, this.min );
+
+ },
+
+ expandByPoint: function ( point ) {
+
+ this.min.min( point );
+ this.max.max( point );
+
+ return this;
+
+ },
+
+ expandByVector: function ( vector ) {
+
+ this.min.sub( vector );
+ this.max.add( vector );
+
+ return this;
+
+ },
+
+ expandByScalar: function ( scalar ) {
+
+ this.min.addScalar( - scalar );
+ this.max.addScalar( scalar );
+
+ return this;
+
+ },
+
+ containsPoint: function ( point ) {
+
+ if ( point.x < this.min.x || point.x > this.max.x ||
+ point.y < this.min.y || point.y > this.max.y ||
+ point.z < this.min.z || point.z > this.max.z ) {
+
+ return false;
+
+ }
+
+ return true;
+
+ },
+
+ containsBox: function ( box ) {
+
+ if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&
+ ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) &&
+ ( this.min.z <= box.min.z ) && ( box.max.z <= this.max.z ) ) {
+
+ return true;
+
+ }
+
+ return false;
+
+ },
+
+ getParameter: function ( point, optionalTarget ) {
+
+ // This can potentially have a divide by zero if the box
+ // has a size dimension of 0.
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ return result.set(
+ ( point.x - this.min.x ) / ( this.max.x - this.min.x ),
+ ( point.y - this.min.y ) / ( this.max.y - this.min.y ),
+ ( point.z - this.min.z ) / ( this.max.z - this.min.z )
+ );
+
+ },
+
+ isIntersectionBox: function ( box ) {
+
+ // using 6 splitting planes to rule out intersections.
+
+ if ( box.max.x < this.min.x || box.min.x > this.max.x ||
+ box.max.y < this.min.y || box.min.y > this.max.y ||
+ box.max.z < this.min.z || box.min.z > this.max.z ) {
+
+ return false;
+
+ }
+
+ return true;
+
+ },
+
+ clampPoint: function ( point, optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.copy( point ).clamp( this.min, this.max );
+
+ },
+
+ distanceToPoint: function () {
+
+ var v1 = new THREE.Vector3();
+
+ return function ( point ) {
+
+ var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
+ return clampedPoint.sub( point ).length();
+
+ };
+
+ }(),
+
+ getBoundingSphere: function () {
+
+ var v1 = new THREE.Vector3();
+
+ return function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Sphere();
+
+ result.center = this.center();
+ result.radius = this.size( v1 ).length() * 0.5;
+
+ return result;
+
+ };
+
+ }(),
+
+ intersect: function ( box ) {
+
+ this.min.max( box.min );
+ this.max.min( box.max );
+
+ return this;
+
+ },
+
+ union: function ( box ) {
+
+ this.min.min( box.min );
+ this.max.max( box.max );
+
+ return this;
+
+ },
+
+ applyMatrix4: function () {
+
+ var points = [
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ new THREE.Vector3()
+ ];
+
+ return function ( matrix ) {
+
+ // NOTE: I am using a binary pattern to specify all 2^3 combinations below
+ points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000
+ points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001
+ points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010
+ points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011
+ points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100
+ points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101
+ points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110
+ points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111
+
+ this.makeEmpty();
+ this.setFromPoints( points );
+
+ return this;
+
+ };
+
+ }(),
+
+ translate: function ( offset ) {
+
+ this.min.add( offset );
+ this.max.add( offset );
+
+ return this;
+
+ },
+
+ equals: function ( box ) {
+
+ return box.min.equals( this.min ) && box.max.equals( this.max );
+
+ }
+
+};
+
+// File:src/math/Matrix3.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Matrix3 = function () {
+
+ this.elements = new Float32Array( [
+
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
+
+ ] );
+
+ if ( arguments.length > 0 ) {
+
+ console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' );
+
+ }
+
+};
+
+THREE.Matrix3.prototype = {
+
+ constructor: THREE.Matrix3,
+
+ set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
+
+ var te = this.elements;
+
+ te[ 0 ] = n11; te[ 3 ] = n12; te[ 6 ] = n13;
+ te[ 1 ] = n21; te[ 4 ] = n22; te[ 7 ] = n23;
+ te[ 2 ] = n31; te[ 5 ] = n32; te[ 8 ] = n33;
+
+ return this;
+
+ },
+
+ identity: function () {
+
+ this.set(
+
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().fromArray( this.elements );
+
+ },
+
+ copy: function ( m ) {
+
+ var me = m.elements;
+
+ this.set(
+
+ me[ 0 ], me[ 3 ], me[ 6 ],
+ me[ 1 ], me[ 4 ], me[ 7 ],
+ me[ 2 ], me[ 5 ], me[ 8 ]
+
+ );
+
+ return this;
+
+ },
+
+ multiplyVector3: function ( vector ) {
+
+ console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' );
+ return vector.applyMatrix3( this );
+
+ },
+
+ multiplyVector3Array: function ( a ) {
+
+ console.warn( 'THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' );
+ return this.applyToVector3Array( a );
+
+ },
+
+ applyToVector3Array: function () {
+
+ var v1;
+
+ return function ( array, offset, length ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+ if ( offset === undefined ) offset = 0;
+ if ( length === undefined ) length = array.length;
+
+ for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) {
+
+ v1.fromArray( array, j );
+ v1.applyMatrix3( this );
+ v1.toArray( array, j );
+
+ }
+
+ return array;
+
+ };
+
+ }(),
+
+ applyToBuffer: function () {
+
+ var v1;
+
+ return function applyToBuffer( buffer, offset, length ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+ if ( offset === undefined ) offset = 0;
+ if ( length === undefined ) length = buffer.length / buffer.itemSize;
+
+ for ( var i = 0, j = offset; i < length; i ++, j ++ ) {
+
+ v1.x = buffer.getX( j );
+ v1.y = buffer.getY( j );
+ v1.z = buffer.getZ( j );
+
+ v1.applyMatrix3( this );
+
+ buffer.setXYZ( v1.x, v1.y, v1.z );
+
+ }
+
+ return buffer;
+
+ };
+
+ }(),
+
+ multiplyScalar: function ( s ) {
+
+ var te = this.elements;
+
+ te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
+ te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
+ te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;
+
+ return this;
+
+ },
+
+ determinant: function () {
+
+ var te = this.elements;
+
+ var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
+ d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
+ g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];
+
+ return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;
+
+ },
+
+ getInverse: function ( matrix, throwOnInvertible ) {
+
+ // input: THREE.Matrix4
+ // ( based on http://code.google.com/p/webgl-mjs/ )
+
+ var me = matrix.elements;
+ var te = this.elements;
+
+ te[ 0 ] = me[ 10 ] * me[ 5 ] - me[ 6 ] * me[ 9 ];
+ te[ 1 ] = - me[ 10 ] * me[ 1 ] + me[ 2 ] * me[ 9 ];
+ te[ 2 ] = me[ 6 ] * me[ 1 ] - me[ 2 ] * me[ 5 ];
+ te[ 3 ] = - me[ 10 ] * me[ 4 ] + me[ 6 ] * me[ 8 ];
+ te[ 4 ] = me[ 10 ] * me[ 0 ] - me[ 2 ] * me[ 8 ];
+ te[ 5 ] = - me[ 6 ] * me[ 0 ] + me[ 2 ] * me[ 4 ];
+ te[ 6 ] = me[ 9 ] * me[ 4 ] - me[ 5 ] * me[ 8 ];
+ te[ 7 ] = - me[ 9 ] * me[ 0 ] + me[ 1 ] * me[ 8 ];
+ te[ 8 ] = me[ 5 ] * me[ 0 ] - me[ 1 ] * me[ 4 ];
+
+ var det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 3 ] + me[ 2 ] * te[ 6 ];
+
+ // no inverse
+
+ if ( det === 0 ) {
+
+ var msg = "Matrix3.getInverse(): can't invert matrix, determinant is 0";
+
+ if ( throwOnInvertible || false ) {
+
+ throw new Error( msg );
+
+ } else {
+
+ console.warn( msg );
+
+ }
+
+ this.identity();
+
+ return this;
+
+ }
+
+ this.multiplyScalar( 1.0 / det );
+
+ return this;
+
+ },
+
+ transpose: function () {
+
+ var tmp, m = this.elements;
+
+ tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
+ tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
+ tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;
+
+ return this;
+
+ },
+
+ flattenToArrayOffset: function ( array, offset ) {
+
+ var te = this.elements;
+
+ array[ offset ] = te[ 0 ];
+ array[ offset + 1 ] = te[ 1 ];
+ array[ offset + 2 ] = te[ 2 ];
+
+ array[ offset + 3 ] = te[ 3 ];
+ array[ offset + 4 ] = te[ 4 ];
+ array[ offset + 5 ] = te[ 5 ];
+
+ array[ offset + 6 ] = te[ 6 ];
+ array[ offset + 7 ] = te[ 7 ];
+ array[ offset + 8 ] = te[ 8 ];
+
+ return array;
+
+ },
+
+ getNormalMatrix: function ( m ) {
+
+ // input: THREE.Matrix4
+
+ this.getInverse( m ).transpose();
+
+ return this;
+
+ },
+
+ transposeIntoArray: function ( r ) {
+
+ var m = this.elements;
+
+ r[ 0 ] = m[ 0 ];
+ r[ 1 ] = m[ 3 ];
+ r[ 2 ] = m[ 6 ];
+ r[ 3 ] = m[ 1 ];
+ r[ 4 ] = m[ 4 ];
+ r[ 5 ] = m[ 7 ];
+ r[ 6 ] = m[ 2 ];
+ r[ 7 ] = m[ 5 ];
+ r[ 8 ] = m[ 8 ];
+
+ return this;
+
+ },
+
+ fromArray: function ( array ) {
+
+ this.elements.set( array );
+
+ return this;
+
+ },
+
+ toArray: function () {
+
+ var te = this.elements;
+
+ return [
+ te[ 0 ], te[ 1 ], te[ 2 ],
+ te[ 3 ], te[ 4 ], te[ 5 ],
+ te[ 6 ], te[ 7 ], te[ 8 ]
+ ];
+
+ }
+
+};
+
+// File:src/math/Matrix4.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author philogb / http://blog.thejit.org/
+ * @author jordi_ros / http://plattsoft.com
+ * @author D1plo1d / http://github.com/D1plo1d
+ * @author alteredq / http://alteredqualia.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author timknip / http://www.floorplanner.com/
+ * @author bhouston / http://clara.io
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.Matrix4 = function () {
+
+ this.elements = new Float32Array( [
+
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+
+ ] );
+
+ if ( arguments.length > 0 ) {
+
+ console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );
+
+ }
+
+};
+
+THREE.Matrix4.prototype = {
+
+ constructor: THREE.Matrix4,
+
+ set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
+
+ var te = this.elements;
+
+ te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
+ te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
+ te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
+ te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;
+
+ return this;
+
+ },
+
+ identity: function () {
+
+ this.set(
+
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new THREE.Matrix4().fromArray( this.elements );
+
+ },
+
+ copy: function ( m ) {
+
+ this.elements.set( m.elements );
+
+ return this;
+
+ },
+
+ extractPosition: function ( m ) {
+
+ console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' );
+ return this.copyPosition( m );
+
+ },
+
+ copyPosition: function ( m ) {
+
+ var te = this.elements;
+ var me = m.elements;
+
+ te[ 12 ] = me[ 12 ];
+ te[ 13 ] = me[ 13 ];
+ te[ 14 ] = me[ 14 ];
+
+ return this;
+
+ },
+
+ extractBasis: function ( xAxis, yAxis, zAxis ) {
+
+ var te = this.elements;
+
+ xAxis.set( te[ 0 ], te[ 1 ], te[ 2 ] );
+ yAxis.set( te[ 4 ], te[ 5 ], te[ 6 ] );
+ zAxis.set( te[ 8 ], te[ 9 ], te[ 10 ] );
+
+ return this;
+
+ },
+
+ makeBasis: function ( xAxis, yAxis, zAxis ) {
+
+ this.set(
+ xAxis.x, yAxis.x, zAxis.x, 0,
+ xAxis.y, yAxis.y, zAxis.y, 0,
+ xAxis.z, yAxis.z, zAxis.z, 0,
+ 0, 0, 0, 1
+ );
+
+ return this;
+
+ },
+
+ extractRotation: function () {
+
+ var v1;
+
+ return function ( m ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ var te = this.elements;
+ var me = m.elements;
+
+ var scaleX = 1 / v1.set( me[ 0 ], me[ 1 ], me[ 2 ] ).length();
+ var scaleY = 1 / v1.set( me[ 4 ], me[ 5 ], me[ 6 ] ).length();
+ var scaleZ = 1 / v1.set( me[ 8 ], me[ 9 ], me[ 10 ] ).length();
+
+ te[ 0 ] = me[ 0 ] * scaleX;
+ te[ 1 ] = me[ 1 ] * scaleX;
+ te[ 2 ] = me[ 2 ] * scaleX;
+
+ te[ 4 ] = me[ 4 ] * scaleY;
+ te[ 5 ] = me[ 5 ] * scaleY;
+ te[ 6 ] = me[ 6 ] * scaleY;
+
+ te[ 8 ] = me[ 8 ] * scaleZ;
+ te[ 9 ] = me[ 9 ] * scaleZ;
+ te[ 10 ] = me[ 10 ] * scaleZ;
+
+ return this;
+
+ };
+
+ }(),
+
+ makeRotationFromEuler: function ( euler ) {
+
+ if ( euler instanceof THREE.Euler === false ) {
+
+ console.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ var te = this.elements;
+
+ var x = euler.x, y = euler.y, z = euler.z;
+ var a = Math.cos( x ), b = Math.sin( x );
+ var c = Math.cos( y ), d = Math.sin( y );
+ var e = Math.cos( z ), f = Math.sin( z );
+
+ if ( euler.order === 'XYZ' ) {
+
+ var ae = a * e, af = a * f, be = b * e, bf = b * f;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = - c * f;
+ te[ 8 ] = d;
+
+ te[ 1 ] = af + be * d;
+ te[ 5 ] = ae - bf * d;
+ te[ 9 ] = - b * c;
+
+ te[ 2 ] = bf - ae * d;
+ te[ 6 ] = be + af * d;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'YXZ' ) {
+
+ var ce = c * e, cf = c * f, de = d * e, df = d * f;
+
+ te[ 0 ] = ce + df * b;
+ te[ 4 ] = de * b - cf;
+ te[ 8 ] = a * d;
+
+ te[ 1 ] = a * f;
+ te[ 5 ] = a * e;
+ te[ 9 ] = - b;
+
+ te[ 2 ] = cf * b - de;
+ te[ 6 ] = df + ce * b;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'ZXY' ) {
+
+ var ce = c * e, cf = c * f, de = d * e, df = d * f;
+
+ te[ 0 ] = ce - df * b;
+ te[ 4 ] = - a * f;
+ te[ 8 ] = de + cf * b;
+
+ te[ 1 ] = cf + de * b;
+ te[ 5 ] = a * e;
+ te[ 9 ] = df - ce * b;
+
+ te[ 2 ] = - a * d;
+ te[ 6 ] = b;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'ZYX' ) {
+
+ var ae = a * e, af = a * f, be = b * e, bf = b * f;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = be * d - af;
+ te[ 8 ] = ae * d + bf;
+
+ te[ 1 ] = c * f;
+ te[ 5 ] = bf * d + ae;
+ te[ 9 ] = af * d - be;
+
+ te[ 2 ] = - d;
+ te[ 6 ] = b * c;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'YZX' ) {
+
+ var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = bd - ac * f;
+ te[ 8 ] = bc * f + ad;
+
+ te[ 1 ] = f;
+ te[ 5 ] = a * e;
+ te[ 9 ] = - b * e;
+
+ te[ 2 ] = - d * e;
+ te[ 6 ] = ad * f + bc;
+ te[ 10 ] = ac - bd * f;
+
+ } else if ( euler.order === 'XZY' ) {
+
+ var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = - f;
+ te[ 8 ] = d * e;
+
+ te[ 1 ] = ac * f + bd;
+ te[ 5 ] = a * e;
+ te[ 9 ] = ad * f - bc;
+
+ te[ 2 ] = bc * f - ad;
+ te[ 6 ] = b * e;
+ te[ 10 ] = bd * f + ac;
+
+ }
+
+ // last column
+ te[ 3 ] = 0;
+ te[ 7 ] = 0;
+ te[ 11 ] = 0;
+
+ // bottom row
+ te[ 12 ] = 0;
+ te[ 13 ] = 0;
+ te[ 14 ] = 0;
+ te[ 15 ] = 1;
+
+ return this;
+
+ },
+
+ setRotationFromQuaternion: function ( q ) {
+
+ console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' );
+
+ return this.makeRotationFromQuaternion( q );
+
+ },
+
+ makeRotationFromQuaternion: function ( q ) {
+
+ var te = this.elements;
+
+ var x = q.x, y = q.y, z = q.z, w = q.w;
+ var x2 = x + x, y2 = y + y, z2 = z + z;
+ var xx = x * x2, xy = x * y2, xz = x * z2;
+ var yy = y * y2, yz = y * z2, zz = z * z2;
+ var wx = w * x2, wy = w * y2, wz = w * z2;
+
+ te[ 0 ] = 1 - ( yy + zz );
+ te[ 4 ] = xy - wz;
+ te[ 8 ] = xz + wy;
+
+ te[ 1 ] = xy + wz;
+ te[ 5 ] = 1 - ( xx + zz );
+ te[ 9 ] = yz - wx;
+
+ te[ 2 ] = xz - wy;
+ te[ 6 ] = yz + wx;
+ te[ 10 ] = 1 - ( xx + yy );
+
+ // last column
+ te[ 3 ] = 0;
+ te[ 7 ] = 0;
+ te[ 11 ] = 0;
+
+ // bottom row
+ te[ 12 ] = 0;
+ te[ 13 ] = 0;
+ te[ 14 ] = 0;
+ te[ 15 ] = 1;
+
+ return this;
+
+ },
+
+ lookAt: function () {
+
+ var x, y, z;
+
+ return function ( eye, target, up ) {
+
+ if ( x === undefined ) x = new THREE.Vector3();
+ if ( y === undefined ) y = new THREE.Vector3();
+ if ( z === undefined ) z = new THREE.Vector3();
+
+ var te = this.elements;
+
+ z.subVectors( eye, target ).normalize();
+
+ if ( z.lengthSq() === 0 ) {
+
+ z.z = 1;
+
+ }
+
+ x.crossVectors( up, z ).normalize();
+
+ if ( x.lengthSq() === 0 ) {
+
+ z.x += 0.0001;
+ x.crossVectors( up, z ).normalize();
+
+ }
+
+ y.crossVectors( z, x );
+
+
+ te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;
+ te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;
+ te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;
+
+ return this;
+
+ };
+
+ }(),
+
+ multiply: function ( m, n ) {
+
+ if ( n !== undefined ) {
+
+ console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
+ return this.multiplyMatrices( m, n );
+
+ }
+
+ return this.multiplyMatrices( this, m );
+
+ },
+
+ multiplyMatrices: function ( a, b ) {
+
+ var ae = a.elements;
+ var be = b.elements;
+ var te = this.elements;
+
+ var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
+ var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
+ var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
+ var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];
+
+ var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
+ var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
+ var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
+ var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];
+
+ te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
+ te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
+ te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
+ te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
+
+ te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
+ te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
+ te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
+ te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
+
+ te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
+ te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
+ te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
+ te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
+
+ te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
+ te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
+ te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
+ te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
+
+ return this;
+
+ },
+
+ multiplyToArray: function ( a, b, r ) {
+
+ var te = this.elements;
+
+ this.multiplyMatrices( a, b );
+
+ r[ 0 ] = te[ 0 ]; r[ 1 ] = te[ 1 ]; r[ 2 ] = te[ 2 ]; r[ 3 ] = te[ 3 ];
+ r[ 4 ] = te[ 4 ]; r[ 5 ] = te[ 5 ]; r[ 6 ] = te[ 6 ]; r[ 7 ] = te[ 7 ];
+ r[ 8 ] = te[ 8 ]; r[ 9 ] = te[ 9 ]; r[ 10 ] = te[ 10 ]; r[ 11 ] = te[ 11 ];
+ r[ 12 ] = te[ 12 ]; r[ 13 ] = te[ 13 ]; r[ 14 ] = te[ 14 ]; r[ 15 ] = te[ 15 ];
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( s ) {
+
+ var te = this.elements;
+
+ te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
+ te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
+ te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
+ te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;
+
+ return this;
+
+ },
+
+ multiplyVector3: function ( vector ) {
+
+ console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' );
+ return vector.applyProjection( this );
+
+ },
+
+ multiplyVector4: function ( vector ) {
+
+ console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
+ return vector.applyMatrix4( this );
+
+ },
+
+ multiplyVector3Array: function ( a ) {
+
+ console.warn( 'THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' );
+ return this.applyToVector3Array( a );
+
+ },
+
+ applyToVector3Array: function () {
+
+ var v1;
+
+ return function ( array, offset, length ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+ if ( offset === undefined ) offset = 0;
+ if ( length === undefined ) length = array.length;
+
+ for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) {
+
+ v1.fromArray( array, j );
+ v1.applyMatrix4( this );
+ v1.toArray( array, j );
+
+ }
+
+ return array;
+
+ };
+
+ }(),
+
+ applyToBuffer: function () {
+
+ var v1;
+
+ return function applyToBuffer( buffer, offset, length ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+ if ( offset === undefined ) offset = 0;
+ if ( length === undefined ) length = buffer.length / buffer.itemSize;
+
+ for ( var i = 0, j = offset; i < length; i ++, j ++ ) {
+
+ v1.x = buffer.getX( j );
+ v1.y = buffer.getY( j );
+ v1.z = buffer.getZ( j );
+
+ v1.applyMatrix4( this );
+
+ buffer.setXYZ( v1.x, v1.y, v1.z );
+
+ }
+
+ return buffer;
+
+ };
+
+ }(),
+
+ rotateAxis: function ( v ) {
+
+ console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' );
+
+ v.transformDirection( this );
+
+ },
+
+ crossVector: function ( vector ) {
+
+ console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
+ return vector.applyMatrix4( this );
+
+ },
+
+ determinant: function () {
+
+ var te = this.elements;
+
+ var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
+ var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
+ var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
+ var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];
+
+ //TODO: make this more efficient
+ //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )
+
+ return (
+ n41 * (
+ + n14 * n23 * n32
+ - n13 * n24 * n32
+ - n14 * n22 * n33
+ + n12 * n24 * n33
+ + n13 * n22 * n34
+ - n12 * n23 * n34
+ ) +
+ n42 * (
+ + n11 * n23 * n34
+ - n11 * n24 * n33
+ + n14 * n21 * n33
+ - n13 * n21 * n34
+ + n13 * n24 * n31
+ - n14 * n23 * n31
+ ) +
+ n43 * (
+ + n11 * n24 * n32
+ - n11 * n22 * n34
+ - n14 * n21 * n32
+ + n12 * n21 * n34
+ + n14 * n22 * n31
+ - n12 * n24 * n31
+ ) +
+ n44 * (
+ - n13 * n22 * n31
+ - n11 * n23 * n32
+ + n11 * n22 * n33
+ + n13 * n21 * n32
+ - n12 * n21 * n33
+ + n12 * n23 * n31
+ )
+
+ );
+
+ },
+
+ transpose: function () {
+
+ var te = this.elements;
+ var tmp;
+
+ tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
+ tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
+ tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;
+
+ tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
+ tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
+ tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;
+
+ return this;
+
+ },
+
+ flattenToArrayOffset: function ( array, offset ) {
+
+ var te = this.elements;
+
+ array[ offset ] = te[ 0 ];
+ array[ offset + 1 ] = te[ 1 ];
+ array[ offset + 2 ] = te[ 2 ];
+ array[ offset + 3 ] = te[ 3 ];
+
+ array[ offset + 4 ] = te[ 4 ];
+ array[ offset + 5 ] = te[ 5 ];
+ array[ offset + 6 ] = te[ 6 ];
+ array[ offset + 7 ] = te[ 7 ];
+
+ array[ offset + 8 ] = te[ 8 ];
+ array[ offset + 9 ] = te[ 9 ];
+ array[ offset + 10 ] = te[ 10 ];
+ array[ offset + 11 ] = te[ 11 ];
+
+ array[ offset + 12 ] = te[ 12 ];
+ array[ offset + 13 ] = te[ 13 ];
+ array[ offset + 14 ] = te[ 14 ];
+ array[ offset + 15 ] = te[ 15 ];
+
+ return array;
+
+ },
+
+ getPosition: function () {
+
+ var v1;
+
+ return function () {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+ console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' );
+
+ var te = this.elements;
+ return v1.set( te[ 12 ], te[ 13 ], te[ 14 ] );
+
+ };
+
+ }(),
+
+ setPosition: function ( v ) {
+
+ var te = this.elements;
+
+ te[ 12 ] = v.x;
+ te[ 13 ] = v.y;
+ te[ 14 ] = v.z;
+
+ return this;
+
+ },
+
+ getInverse: function ( m, throwOnInvertible ) {
+
+ // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
+ var te = this.elements;
+ var me = m.elements;
+
+ var n11 = me[ 0 ], n12 = me[ 4 ], n13 = me[ 8 ], n14 = me[ 12 ];
+ var n21 = me[ 1 ], n22 = me[ 5 ], n23 = me[ 9 ], n24 = me[ 13 ];
+ var n31 = me[ 2 ], n32 = me[ 6 ], n33 = me[ 10 ], n34 = me[ 14 ];
+ var n41 = me[ 3 ], n42 = me[ 7 ], n43 = me[ 11 ], n44 = me[ 15 ];
+
+ te[ 0 ] = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44;
+ te[ 4 ] = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44;
+ te[ 8 ] = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44;
+ te[ 12 ] = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
+ te[ 1 ] = n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44;
+ te[ 5 ] = n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44;
+ te[ 9 ] = n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44;
+ te[ 13 ] = n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34;
+ te[ 2 ] = n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44;
+ te[ 6 ] = n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44;
+ te[ 10 ] = n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44;
+ te[ 14 ] = n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34;
+ te[ 3 ] = n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43;
+ te[ 7 ] = n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43;
+ te[ 11 ] = n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43;
+ te[ 15 ] = n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33;
+
+ var det = n11 * te[ 0 ] + n21 * te[ 4 ] + n31 * te[ 8 ] + n41 * te[ 12 ];
+
+ if ( det === 0 ) {
+
+ var msg = "THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0";
+
+ if ( throwOnInvertible || false ) {
+
+ throw new Error( msg );
+
+ } else {
+
+ console.warn( msg );
+
+ }
+
+ this.identity();
+
+ return this;
+
+ }
+
+ this.multiplyScalar( 1 / det );
+
+ return this;
+
+ },
+
+ translate: function ( v ) {
+
+ console.error( 'THREE.Matrix4: .translate() has been removed.' );
+
+ },
+
+ rotateX: function ( angle ) {
+
+ console.error( 'THREE.Matrix4: .rotateX() has been removed.' );
+
+ },
+
+ rotateY: function ( angle ) {
+
+ console.error( 'THREE.Matrix4: .rotateY() has been removed.' );
+
+ },
+
+ rotateZ: function ( angle ) {
+
+ console.error( 'THREE.Matrix4: .rotateZ() has been removed.' );
+
+ },
+
+ rotateByAxis: function ( axis, angle ) {
+
+ console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' );
+
+ },
+
+ scale: function ( v ) {
+
+ var te = this.elements;
+ var x = v.x, y = v.y, z = v.z;
+
+ te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;
+ te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;
+ te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;
+ te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;
+
+ return this;
+
+ },
+
+ getMaxScaleOnAxis: function () {
+
+ var te = this.elements;
+
+ var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];
+ var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];
+ var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];
+
+ return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );
+
+ },
+
+ makeTranslation: function ( x, y, z ) {
+
+ this.set(
+
+ 1, 0, 0, x,
+ 0, 1, 0, y,
+ 0, 0, 1, z,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationX: function ( theta ) {
+
+ var c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ 1, 0, 0, 0,
+ 0, c, - s, 0,
+ 0, s, c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationY: function ( theta ) {
+
+ var c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ c, 0, s, 0,
+ 0, 1, 0, 0,
+ - s, 0, c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationZ: function ( theta ) {
+
+ var c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ c, - s, 0, 0,
+ s, c, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationAxis: function ( axis, angle ) {
+
+ // Based on http://www.gamedev.net/reference/articles/article1199.asp
+
+ var c = Math.cos( angle );
+ var s = Math.sin( angle );
+ var t = 1 - c;
+ var x = axis.x, y = axis.y, z = axis.z;
+ var tx = t * x, ty = t * y;
+
+ this.set(
+
+ tx * x + c, tx * y - s * z, tx * z + s * y, 0,
+ tx * y + s * z, ty * y + c, ty * z - s * x, 0,
+ tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeScale: function ( x, y, z ) {
+
+ this.set(
+
+ x, 0, 0, 0,
+ 0, y, 0, 0,
+ 0, 0, z, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ compose: function ( position, quaternion, scale ) {
+
+ this.makeRotationFromQuaternion( quaternion );
+ this.scale( scale );
+ this.setPosition( position );
+
+ return this;
+
+ },
+
+ decompose: function () {
+
+ var vector, matrix;
+
+ return function ( position, quaternion, scale ) {
+
+ if ( vector === undefined ) vector = new THREE.Vector3();
+ if ( matrix === undefined ) matrix = new THREE.Matrix4();
+
+ var te = this.elements;
+
+ var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
+ var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
+ var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();
+
+ // if determine is negative, we need to invert one scale
+ var det = this.determinant();
+ if ( det < 0 ) {
+
+ sx = - sx;
+
+ }
+
+ position.x = te[ 12 ];
+ position.y = te[ 13 ];
+ position.z = te[ 14 ];
+
+ // scale the rotation part
+
+ matrix.elements.set( this.elements ); // at this point matrix is incomplete so we can't use .copy()
+
+ var invSX = 1 / sx;
+ var invSY = 1 / sy;
+ var invSZ = 1 / sz;
+
+ matrix.elements[ 0 ] *= invSX;
+ matrix.elements[ 1 ] *= invSX;
+ matrix.elements[ 2 ] *= invSX;
+
+ matrix.elements[ 4 ] *= invSY;
+ matrix.elements[ 5 ] *= invSY;
+ matrix.elements[ 6 ] *= invSY;
+
+ matrix.elements[ 8 ] *= invSZ;
+ matrix.elements[ 9 ] *= invSZ;
+ matrix.elements[ 10 ] *= invSZ;
+
+ quaternion.setFromRotationMatrix( matrix );
+
+ scale.x = sx;
+ scale.y = sy;
+ scale.z = sz;
+
+ return this;
+
+ };
+
+ }(),
+
+ makeFrustum: function ( left, right, bottom, top, near, far ) {
+
+ var te = this.elements;
+ var x = 2 * near / ( right - left );
+ var y = 2 * near / ( top - bottom );
+
+ var a = ( right + left ) / ( right - left );
+ var b = ( top + bottom ) / ( top - bottom );
+ var c = - ( far + near ) / ( far - near );
+ var d = - 2 * far * near / ( far - near );
+
+ te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0;
+ te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0;
+ te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d;
+ te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0;
+
+ return this;
+
+ },
+
+ makePerspective: function ( fov, aspect, near, far ) {
+
+ var ymax = near * Math.tan( THREE.Math.degToRad( fov * 0.5 ) );
+ var ymin = - ymax;
+ var xmin = ymin * aspect;
+ var xmax = ymax * aspect;
+
+ return this.makeFrustum( xmin, xmax, ymin, ymax, near, far );
+
+ },
+
+ makeOrthographic: function ( left, right, top, bottom, near, far ) {
+
+ var te = this.elements;
+ var w = right - left;
+ var h = top - bottom;
+ var p = far - near;
+
+ var x = ( right + left ) / w;
+ var y = ( top + bottom ) / h;
+ var z = ( far + near ) / p;
+
+ te[ 0 ] = 2 / w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x;
+ te[ 1 ] = 0; te[ 5 ] = 2 / h; te[ 9 ] = 0; te[ 13 ] = - y;
+ te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 / p; te[ 14 ] = - z;
+ te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1;
+
+ return this;
+
+ },
+
+ equals: function ( matrix ) {
+
+ var te = this.elements;
+ var me = matrix.elements;
+
+ for ( var i = 0; i < 16; i ++ ) {
+
+ if ( te[ i ] !== me[ i ] ) return false;
+
+ }
+
+ return true;
+
+ },
+
+ fromArray: function ( array ) {
+
+ this.elements.set( array );
+
+ return this;
+
+ },
+
+ toArray: function () {
+
+ var te = this.elements;
+
+ return [
+ te[ 0 ], te[ 1 ], te[ 2 ], te[ 3 ],
+ te[ 4 ], te[ 5 ], te[ 6 ], te[ 7 ],
+ te[ 8 ], te[ 9 ], te[ 10 ], te[ 11 ],
+ te[ 12 ], te[ 13 ], te[ 14 ], te[ 15 ]
+ ];
+
+ }
+
+};
+
+// File:src/math/Ray.js
+
+/**
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Ray = function ( origin, direction ) {
+
+ this.origin = ( origin !== undefined ) ? origin : new THREE.Vector3();
+ this.direction = ( direction !== undefined ) ? direction : new THREE.Vector3();
+
+};
+
+THREE.Ray.prototype = {
+
+ constructor: THREE.Ray,
+
+ set: function ( origin, direction ) {
+
+ this.origin.copy( origin );
+ this.direction.copy( direction );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( ray ) {
+
+ this.origin.copy( ray.origin );
+ this.direction.copy( ray.direction );
+
+ return this;
+
+ },
+
+ at: function ( t, optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ return result.copy( this.direction ).multiplyScalar( t ).add( this.origin );
+
+ },
+
+ recast: function () {
+
+ var v1 = new THREE.Vector3();
+
+ return function ( t ) {
+
+ this.origin.copy( this.at( t, v1 ) );
+
+ return this;
+
+ };
+
+ }(),
+
+ closestPointToPoint: function ( point, optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ result.subVectors( point, this.origin );
+ var directionDistance = result.dot( this.direction );
+
+ if ( directionDistance < 0 ) {
+
+ return result.copy( this.origin );
+
+ }
+
+ return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
+
+ },
+
+ distanceToPoint: function ( point ) {
+
+ return Math.sqrt( this.distanceSqToPoint( point ) );
+
+ },
+
+ distanceSqToPoint: function () {
+
+ var v1 = new THREE.Vector3();
+
+ return function ( point ) {
+
+ var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction );
+
+ // point behind the ray
+
+ if ( directionDistance < 0 ) {
+
+ return this.origin.distanceToSquared( point );
+
+ }
+
+ v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
+
+ return v1.distanceToSquared( point );
+
+ };
+
+ }(),
+
+ distanceSqToSegment: function () {
+
+ var segCenter = new THREE.Vector3();
+ var segDir = new THREE.Vector3();
+ var diff = new THREE.Vector3();
+
+ return function ( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {
+
+ // from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp
+ // It returns the min distance between the ray and the segment
+ // defined by v0 and v1
+ // It can also set two optional targets :
+ // - The closest point on the ray
+ // - The closest point on the segment
+
+ segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );
+ segDir.copy( v1 ).sub( v0 ).normalize();
+ diff.copy( this.origin ).sub( segCenter );
+
+ var segExtent = v0.distanceTo( v1 ) * 0.5;
+ var a01 = - this.direction.dot( segDir );
+ var b0 = diff.dot( this.direction );
+ var b1 = - diff.dot( segDir );
+ var c = diff.lengthSq();
+ var det = Math.abs( 1 - a01 * a01 );
+ var s0, s1, sqrDist, extDet;
+
+ if ( det > 0 ) {
+
+ // The ray and segment are not parallel.
+
+ s0 = a01 * b1 - b0;
+ s1 = a01 * b0 - b1;
+ extDet = segExtent * det;
+
+ if ( s0 >= 0 ) {
+
+ if ( s1 >= - extDet ) {
+
+ if ( s1 <= extDet ) {
+
+ // region 0
+ // Minimum at interior points of ray and segment.
+
+ var invDet = 1 / det;
+ s0 *= invDet;
+ s1 *= invDet;
+ sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;
+
+ } else {
+
+ // region 1
+
+ s1 = segExtent;
+ s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ }
+
+ } else {
+
+ // region 5
+
+ s1 = - segExtent;
+ s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ }
+
+ } else {
+
+ if ( s1 <= - extDet ) {
+
+ // region 4
+
+ s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );
+ s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ } else if ( s1 <= extDet ) {
+
+ // region 3
+
+ s0 = 0;
+ s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );
+ sqrDist = s1 * ( s1 + 2 * b1 ) + c;
+
+ } else {
+
+ // region 2
+
+ s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );
+ s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ }
+
+ }
+
+ } else {
+
+ // Ray and segment are parallel.
+
+ s1 = ( a01 > 0 ) ? - segExtent : segExtent;
+ s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ }
+
+ if ( optionalPointOnRay ) {
+
+ optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin );
+
+ }
+
+ if ( optionalPointOnSegment ) {
+
+ optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter );
+
+ }
+
+ return sqrDist;
+
+ };
+
+ }(),
+
+
+ isIntersectionSphere: function ( sphere ) {
+
+ return this.distanceToPoint( sphere.center ) <= sphere.radius;
+
+ },
+
+ intersectSphere: function () {
+
+ // from http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-sphere-intersection/
+
+ var v1 = new THREE.Vector3();
+
+ return function ( sphere, optionalTarget ) {
+
+ v1.subVectors( sphere.center, this.origin );
+
+ var tca = v1.dot( this.direction );
+
+ var d2 = v1.dot( v1 ) - tca * tca;
+
+ var radius2 = sphere.radius * sphere.radius;
+
+ if ( d2 > radius2 ) return null;
+
+ var thc = Math.sqrt( radius2 - d2 );
+
+ // t0 = first intersect point - entrance on front of sphere
+ var t0 = tca - thc;
+
+ // t1 = second intersect point - exit point on back of sphere
+ var t1 = tca + thc;
+
+ // test to see if both t0 and t1 are behind the ray - if so, return null
+ if ( t0 < 0 && t1 < 0 ) return null;
+
+ // test to see if t0 is behind the ray:
+ // if it is, the ray is inside the sphere, so return the second exit point scaled by t1,
+ // in order to always return an intersect point that is in front of the ray.
+ if ( t0 < 0 ) return this.at( t1, optionalTarget );
+
+ // else t0 is in front of the ray, so return the first collision point scaled by t0
+ return this.at( t0, optionalTarget );
+
+ }
+
+ }(),
+
+ isIntersectionPlane: function ( plane ) {
+
+ // check if the ray lies on the plane first
+
+ var distToPoint = plane.distanceToPoint( this.origin );
+
+ if ( distToPoint === 0 ) {
+
+ return true;
+
+ }
+
+ var denominator = plane.normal.dot( this.direction );
+
+ if ( denominator * distToPoint < 0 ) {
+
+ return true;
+
+ }
+
+ // ray origin is behind the plane (and is pointing behind it)
+
+ return false;
+
+ },
+
+ distanceToPlane: function ( plane ) {
+
+ var denominator = plane.normal.dot( this.direction );
+ if ( denominator === 0 ) {
+
+ // line is coplanar, return origin
+ if ( plane.distanceToPoint( this.origin ) === 0 ) {
+
+ return 0;
+
+ }
+
+ // Null is preferable to undefined since undefined means.... it is undefined
+
+ return null;
+
+ }
+
+ var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;
+
+ // Return if the ray never intersects the plane
+
+ return t >= 0 ? t : null;
+
+ },
+
+ intersectPlane: function ( plane, optionalTarget ) {
+
+ var t = this.distanceToPlane( plane );
+
+ if ( t === null ) {
+
+ return null;
+
+ }
+
+ return this.at( t, optionalTarget );
+
+ },
+
+ isIntersectionBox: function () {
+
+ var v = new THREE.Vector3();
+
+ return function ( box ) {
+
+ return this.intersectBox( box, v ) !== null;
+
+ };
+
+ }(),
+
+ intersectBox: function ( box, optionalTarget ) {
+
+ // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/
+
+ var tmin, tmax, tymin, tymax, tzmin, tzmax;
+
+ var invdirx = 1 / this.direction.x,
+ invdiry = 1 / this.direction.y,
+ invdirz = 1 / this.direction.z;
+
+ var origin = this.origin;
+
+ if ( invdirx >= 0 ) {
+
+ tmin = ( box.min.x - origin.x ) * invdirx;
+ tmax = ( box.max.x - origin.x ) * invdirx;
+
+ } else {
+
+ tmin = ( box.max.x - origin.x ) * invdirx;
+ tmax = ( box.min.x - origin.x ) * invdirx;
+
+ }
+
+ if ( invdiry >= 0 ) {
+
+ tymin = ( box.min.y - origin.y ) * invdiry;
+ tymax = ( box.max.y - origin.y ) * invdiry;
+
+ } else {
+
+ tymin = ( box.max.y - origin.y ) * invdiry;
+ tymax = ( box.min.y - origin.y ) * invdiry;
+
+ }
+
+ if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;
+
+ // These lines also handle the case where tmin or tmax is NaN
+ // (result of 0 * Infinity). x !== x returns true if x is NaN
+
+ if ( tymin > tmin || tmin !== tmin ) tmin = tymin;
+
+ if ( tymax < tmax || tmax !== tmax ) tmax = tymax;
+
+ if ( invdirz >= 0 ) {
+
+ tzmin = ( box.min.z - origin.z ) * invdirz;
+ tzmax = ( box.max.z - origin.z ) * invdirz;
+
+ } else {
+
+ tzmin = ( box.max.z - origin.z ) * invdirz;
+ tzmax = ( box.min.z - origin.z ) * invdirz;
+
+ }
+
+ if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;
+
+ if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;
+
+ if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;
+
+ //return point closest to the ray (positive side)
+
+ if ( tmax < 0 ) return null;
+
+ return this.at( tmin >= 0 ? tmin : tmax, optionalTarget );
+
+ },
+
+ intersectTriangle: function () {
+
+ // Compute the offset origin, edges, and normal.
+ var diff = new THREE.Vector3();
+ var edge1 = new THREE.Vector3();
+ var edge2 = new THREE.Vector3();
+ var normal = new THREE.Vector3();
+
+ return function ( a, b, c, backfaceCulling, optionalTarget ) {
+
+ // from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrRay3Triangle3.cpp
+
+ edge1.subVectors( b, a );
+ edge2.subVectors( c, a );
+ normal.crossVectors( edge1, edge2 );
+
+ // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,
+ // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by
+ // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))
+ // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))
+ // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)
+ var DdN = this.direction.dot( normal );
+ var sign;
+
+ if ( DdN > 0 ) {
+
+ if ( backfaceCulling ) return null;
+ sign = 1;
+
+ } else if ( DdN < 0 ) {
+
+ sign = - 1;
+ DdN = - DdN;
+
+ } else {
+
+ return null;
+
+ }
+
+ diff.subVectors( this.origin, a );
+ var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) );
+
+ // b1 < 0, no intersection
+ if ( DdQxE2 < 0 ) {
+
+ return null;
+
+ }
+
+ var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) );
+
+ // b2 < 0, no intersection
+ if ( DdE1xQ < 0 ) {
+
+ return null;
+
+ }
+
+ // b1+b2 > 1, no intersection
+ if ( DdQxE2 + DdE1xQ > DdN ) {
+
+ return null;
+
+ }
+
+ // Line intersects triangle, check if ray does.
+ var QdN = - sign * diff.dot( normal );
+
+ // t < 0, no intersection
+ if ( QdN < 0 ) {
+
+ return null;
+
+ }
+
+ // Ray intersects triangle.
+ return this.at( QdN / DdN, optionalTarget );
+
+ };
+
+ }(),
+
+ applyMatrix4: function ( matrix4 ) {
+
+ this.direction.add( this.origin ).applyMatrix4( matrix4 );
+ this.origin.applyMatrix4( matrix4 );
+ this.direction.sub( this.origin );
+ this.direction.normalize();
+
+ return this;
+
+ },
+
+ equals: function ( ray ) {
+
+ return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );
+
+ }
+
+};
+
+// File:src/math/Sphere.js
+
+/**
+ * @author bhouston / http://clara.io
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Sphere = function ( center, radius ) {
+
+ this.center = ( center !== undefined ) ? center : new THREE.Vector3();
+ this.radius = ( radius !== undefined ) ? radius : 0;
+
+};
+
+THREE.Sphere.prototype = {
+
+ constructor: THREE.Sphere,
+
+ set: function ( center, radius ) {
+
+ this.center.copy( center );
+ this.radius = radius;
+
+ return this;
+
+ },
+
+ setFromPoints: function () {
+
+ var box = new THREE.Box3();
+
+ return function ( points, optionalCenter ) {
+
+ var center = this.center;
+
+ if ( optionalCenter !== undefined ) {
+
+ center.copy( optionalCenter );
+
+ } else {
+
+ box.setFromPoints( points ).center( center );
+
+ }
+
+ var maxRadiusSq = 0;
+
+ for ( var i = 0, il = points.length; i < il; i ++ ) {
+
+ maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) );
+
+ }
+
+ this.radius = Math.sqrt( maxRadiusSq );
+
+ return this;
+
+ };
+
+ }(),
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( sphere ) {
+
+ this.center.copy( sphere.center );
+ this.radius = sphere.radius;
+
+ return this;
+
+ },
+
+ empty: function () {
+
+ return ( this.radius <= 0 );
+
+ },
+
+ containsPoint: function ( point ) {
+
+ return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) );
+
+ },
+
+ distanceToPoint: function ( point ) {
+
+ return ( point.distanceTo( this.center ) - this.radius );
+
+ },
+
+ intersectsSphere: function ( sphere ) {
+
+ var radiusSum = this.radius + sphere.radius;
+
+ return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum );
+
+ },
+
+ clampPoint: function ( point, optionalTarget ) {
+
+ var deltaLengthSq = this.center.distanceToSquared( point );
+
+ var result = optionalTarget || new THREE.Vector3();
+ result.copy( point );
+
+ if ( deltaLengthSq > ( this.radius * this.radius ) ) {
+
+ result.sub( this.center ).normalize();
+ result.multiplyScalar( this.radius ).add( this.center );
+
+ }
+
+ return result;
+
+ },
+
+ getBoundingBox: function ( optionalTarget ) {
+
+ var box = optionalTarget || new THREE.Box3();
+
+ box.set( this.center, this.center );
+ box.expandByScalar( this.radius );
+
+ return box;
+
+ },
+
+ applyMatrix4: function ( matrix ) {
+
+ this.center.applyMatrix4( matrix );
+ this.radius = this.radius * matrix.getMaxScaleOnAxis();
+
+ return this;
+
+ },
+
+ translate: function ( offset ) {
+
+ this.center.add( offset );
+
+ return this;
+
+ },
+
+ equals: function ( sphere ) {
+
+ return sphere.center.equals( this.center ) && ( sphere.radius === this.radius );
+
+ }
+
+};
+
+// File:src/math/Frustum.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Frustum = function ( p0, p1, p2, p3, p4, p5 ) {
+
+ this.planes = [
+
+ ( p0 !== undefined ) ? p0 : new THREE.Plane(),
+ ( p1 !== undefined ) ? p1 : new THREE.Plane(),
+ ( p2 !== undefined ) ? p2 : new THREE.Plane(),
+ ( p3 !== undefined ) ? p3 : new THREE.Plane(),
+ ( p4 !== undefined ) ? p4 : new THREE.Plane(),
+ ( p5 !== undefined ) ? p5 : new THREE.Plane()
+
+ ];
+
+};
+
+THREE.Frustum.prototype = {
+
+ constructor: THREE.Frustum,
+
+ set: function ( p0, p1, p2, p3, p4, p5 ) {
+
+ var planes = this.planes;
+
+ planes[ 0 ].copy( p0 );
+ planes[ 1 ].copy( p1 );
+ planes[ 2 ].copy( p2 );
+ planes[ 3 ].copy( p3 );
+ planes[ 4 ].copy( p4 );
+ planes[ 5 ].copy( p5 );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( frustum ) {
+
+ var planes = this.planes;
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ planes[ i ].copy( frustum.planes[ i ] );
+
+ }
+
+ return this;
+
+ },
+
+ setFromMatrix: function ( m ) {
+
+ var planes = this.planes;
+ var me = m.elements;
+ var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];
+ var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];
+ var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];
+ var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];
+
+ planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();
+ planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();
+ planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();
+ planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();
+ planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();
+ planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize();
+
+ return this;
+
+ },
+
+ intersectsObject: function () {
+
+ var sphere = new THREE.Sphere();
+
+ return function ( object ) {
+
+ var geometry = object.geometry;
+
+ if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
+
+ sphere.copy( geometry.boundingSphere );
+ sphere.applyMatrix4( object.matrixWorld );
+
+ return this.intersectsSphere( sphere );
+
+ };
+
+ }(),
+
+ intersectsSphere: function ( sphere ) {
+
+ var planes = this.planes;
+ var center = sphere.center;
+ var negRadius = - sphere.radius;
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ var distance = planes[ i ].distanceToPoint( center );
+
+ if ( distance < negRadius ) {
+
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+ },
+
+ intersectsBox: function () {
+
+ var p1 = new THREE.Vector3(),
+ p2 = new THREE.Vector3();
+
+ return function ( box ) {
+
+ var planes = this.planes;
+
+ for ( var i = 0; i < 6 ; i ++ ) {
+
+ var plane = planes[ i ];
+
+ p1.x = plane.normal.x > 0 ? box.min.x : box.max.x;
+ p2.x = plane.normal.x > 0 ? box.max.x : box.min.x;
+ p1.y = plane.normal.y > 0 ? box.min.y : box.max.y;
+ p2.y = plane.normal.y > 0 ? box.max.y : box.min.y;
+ p1.z = plane.normal.z > 0 ? box.min.z : box.max.z;
+ p2.z = plane.normal.z > 0 ? box.max.z : box.min.z;
+
+ var d1 = plane.distanceToPoint( p1 );
+ var d2 = plane.distanceToPoint( p2 );
+
+ // if both outside plane, no intersection
+
+ if ( d1 < 0 && d2 < 0 ) {
+
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+ };
+
+ }(),
+
+
+ containsPoint: function ( point ) {
+
+ var planes = this.planes;
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ if ( planes[ i ].distanceToPoint( point ) < 0 ) {
+
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+ }
+
+};
+
+// File:src/math/Plane.js
+
+/**
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Plane = function ( normal, constant ) {
+
+ this.normal = ( normal !== undefined ) ? normal : new THREE.Vector3( 1, 0, 0 );
+ this.constant = ( constant !== undefined ) ? constant : 0;
+
+};
+
+THREE.Plane.prototype = {
+
+ constructor: THREE.Plane,
+
+ set: function ( normal, constant ) {
+
+ this.normal.copy( normal );
+ this.constant = constant;
+
+ return this;
+
+ },
+
+ setComponents: function ( x, y, z, w ) {
+
+ this.normal.set( x, y, z );
+ this.constant = w;
+
+ return this;
+
+ },
+
+ setFromNormalAndCoplanarPoint: function ( normal, point ) {
+
+ this.normal.copy( normal );
+ this.constant = - point.dot( this.normal ); // must be this.normal, not normal, as this.normal is normalized
+
+ return this;
+
+ },
+
+ setFromCoplanarPoints: function () {
+
+ var v1 = new THREE.Vector3();
+ var v2 = new THREE.Vector3();
+
+ return function ( a, b, c ) {
+
+ var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize();
+
+ // Q: should an error be thrown if normal is zero (e.g. degenerate plane)?
+
+ this.setFromNormalAndCoplanarPoint( normal, a );
+
+ return this;
+
+ };
+
+ }(),
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( plane ) {
+
+ this.normal.copy( plane.normal );
+ this.constant = plane.constant;
+
+ return this;
+
+ },
+
+ normalize: function () {
+
+ // Note: will lead to a divide by zero if the plane is invalid.
+
+ var inverseNormalLength = 1.0 / this.normal.length();
+ this.normal.multiplyScalar( inverseNormalLength );
+ this.constant *= inverseNormalLength;
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.constant *= - 1;
+ this.normal.negate();
+
+ return this;
+
+ },
+
+ distanceToPoint: function ( point ) {
+
+ return this.normal.dot( point ) + this.constant;
+
+ },
+
+ distanceToSphere: function ( sphere ) {
+
+ return this.distanceToPoint( sphere.center ) - sphere.radius;
+
+ },
+
+ projectPoint: function ( point, optionalTarget ) {
+
+ return this.orthoPoint( point, optionalTarget ).sub( point ).negate();
+
+ },
+
+ orthoPoint: function ( point, optionalTarget ) {
+
+ var perpendicularMagnitude = this.distanceToPoint( point );
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.copy( this.normal ).multiplyScalar( perpendicularMagnitude );
+
+ },
+
+ isIntersectionLine: function ( line ) {
+
+ // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.
+
+ var startSign = this.distanceToPoint( line.start );
+ var endSign = this.distanceToPoint( line.end );
+
+ return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );
+
+ },
+
+ intersectLine: function () {
+
+ var v1 = new THREE.Vector3();
+
+ return function ( line, optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ var direction = line.delta( v1 );
+
+ var denominator = this.normal.dot( direction );
+
+ if ( denominator === 0 ) {
+
+ // line is coplanar, return origin
+ if ( this.distanceToPoint( line.start ) === 0 ) {
+
+ return result.copy( line.start );
+
+ }
+
+ // Unsure if this is the correct method to handle this case.
+ return undefined;
+
+ }
+
+ var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;
+
+ if ( t < 0 || t > 1 ) {
+
+ return undefined;
+
+ }
+
+ return result.copy( direction ).multiplyScalar( t ).add( line.start );
+
+ };
+
+ }(),
+
+
+ coplanarPoint: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.copy( this.normal ).multiplyScalar( - this.constant );
+
+ },
+
+ applyMatrix4: function () {
+
+ var v1 = new THREE.Vector3();
+ var v2 = new THREE.Vector3();
+ var m1 = new THREE.Matrix3();
+
+ return function ( matrix, optionalNormalMatrix ) {
+
+ // compute new normal based on theory here:
+ // http://www.songho.ca/opengl/gl_normaltransform.html
+ var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix );
+ var newNormal = v1.copy( this.normal ).applyMatrix3( normalMatrix );
+
+ var newCoplanarPoint = this.coplanarPoint( v2 );
+ newCoplanarPoint.applyMatrix4( matrix );
+
+ this.setFromNormalAndCoplanarPoint( newNormal, newCoplanarPoint );
+
+ return this;
+
+ };
+
+ }(),
+
+ translate: function ( offset ) {
+
+ this.constant = this.constant - offset.dot( this.normal );
+
+ return this;
+
+ },
+
+ equals: function ( plane ) {
+
+ return plane.normal.equals( this.normal ) && ( plane.constant === this.constant );
+
+ }
+
+};
+
+// File:src/math/Math.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Math = {
+
+ generateUUID: function () {
+
+ // http://www.broofa.com/Tools/Math.uuid.htm
+
+ var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' );
+ var uuid = new Array( 36 );
+ var rnd = 0, r;
+
+ return function () {
+
+ for ( var i = 0; i < 36; i ++ ) {
+
+ if ( i === 8 || i === 13 || i === 18 || i === 23 ) {
+
+ uuid[ i ] = '-';
+
+ } else if ( i === 14 ) {
+
+ uuid[ i ] = '4';
+
+ } else {
+
+ if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
+ r = rnd & 0xf;
+ rnd = rnd >> 4;
+ uuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ];
+
+ }
+
+ }
+
+ return uuid.join( '' );
+
+ };
+
+ }(),
+
+ clamp: function ( value, min, max ) {
+
+ return Math.max( min, Math.min( max, value ) );
+
+ },
+
+ // compute euclidian modulo of m % n
+ // https://en.wikipedia.org/wiki/Modulo_operation
+
+ euclideanModulo: function ( n, m ) {
+
+ return ( ( n % m ) + m ) % m;
+
+ },
+
+ // Linear mapping from range to range
+
+ mapLinear: function ( x, a1, a2, b1, b2 ) {
+
+ return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
+
+ },
+
+ // http://en.wikipedia.org/wiki/Smoothstep
+
+ smoothstep: function ( x, min, max ) {
+
+ if ( x <= min ) return 0;
+ if ( x >= max ) return 1;
+
+ x = ( x - min ) / ( max - min );
+
+ return x * x * ( 3 - 2 * x );
+
+ },
+
+ smootherstep: function ( x, min, max ) {
+
+ if ( x <= min ) return 0;
+ if ( x >= max ) return 1;
+
+ x = ( x - min ) / ( max - min );
+
+ return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
+
+ },
+
+ // Random float from <0, 1> with 16 bits of randomness
+ // (standard Math.random() creates repetitive patterns when applied over larger space)
+
+ random16: function () {
+
+ return ( 65280 * Math.random() + 255 * Math.random() ) / 65535;
+
+ },
+
+ // Random integer from interval
+
+ randInt: function ( low, high ) {
+
+ return low + Math.floor( Math.random() * ( high - low + 1 ) );
+
+ },
+
+ // Random float from interval
+
+ randFloat: function ( low, high ) {
+
+ return low + Math.random() * ( high - low );
+
+ },
+
+ // Random float from <-range/2, range/2> interval
+
+ randFloatSpread: function ( range ) {
+
+ return range * ( 0.5 - Math.random() );
+
+ },
+
+ degToRad: function () {
+
+ var degreeToRadiansFactor = Math.PI / 180;
+
+ return function ( degrees ) {
+
+ return degrees * degreeToRadiansFactor;
+
+ };
+
+ }(),
+
+ radToDeg: function () {
+
+ var radianToDegreesFactor = 180 / Math.PI;
+
+ return function ( radians ) {
+
+ return radians * radianToDegreesFactor;
+
+ };
+
+ }(),
+
+ isPowerOfTwo: function ( value ) {
+
+ return ( value & ( value - 1 ) ) === 0 && value !== 0;
+
+ },
+
+ nearestPowerOfTwo: function ( value ) {
+
+ return Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) );
+
+ },
+
+ nextPowerOfTwo: function ( value ) {
+
+ value --;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ value ++;
+
+ return value;
+
+ }
+
+};
+
+// File:src/math/Spline.js
+
+/**
+ * Spline from Tween.js, slightly optimized (and trashed)
+ * http://sole.github.com/tween.js/examples/05_spline.html
+ *
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Spline = function ( points ) {
+
+ this.points = points;
+
+ var c = [], v3 = { x: 0, y: 0, z: 0 },
+ point, intPoint, weight, w2, w3,
+ pa, pb, pc, pd;
+
+ this.initFromArray = function ( a ) {
+
+ this.points = [];
+
+ for ( var i = 0; i < a.length; i ++ ) {
+
+ this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] };
+
+ }
+
+ };
+
+ this.getPoint = function ( k ) {
+
+ point = ( this.points.length - 1 ) * k;
+ intPoint = Math.floor( point );
+ weight = point - intPoint;
+
+ c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1;
+ c[ 1 ] = intPoint;
+ c[ 2 ] = intPoint > this.points.length - 2 ? this.points.length - 1 : intPoint + 1;
+ c[ 3 ] = intPoint > this.points.length - 3 ? this.points.length - 1 : intPoint + 2;
+
+ pa = this.points[ c[ 0 ] ];
+ pb = this.points[ c[ 1 ] ];
+ pc = this.points[ c[ 2 ] ];
+ pd = this.points[ c[ 3 ] ];
+
+ w2 = weight * weight;
+ w3 = weight * w2;
+
+ v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 );
+ v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 );
+ v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 );
+
+ return v3;
+
+ };
+
+ this.getControlPointsArray = function () {
+
+ var i, p, l = this.points.length,
+ coords = [];
+
+ for ( i = 0; i < l; i ++ ) {
+
+ p = this.points[ i ];
+ coords[ i ] = [ p.x, p.y, p.z ];
+
+ }
+
+ return coords;
+
+ };
+
+ // approximate length by summing linear segments
+
+ this.getLength = function ( nSubDivisions ) {
+
+ var i, index, nSamples, position,
+ point = 0, intPoint = 0, oldIntPoint = 0,
+ oldPosition = new THREE.Vector3(),
+ tmpVec = new THREE.Vector3(),
+ chunkLengths = [],
+ totalLength = 0;
+
+ // first point has 0 length
+
+ chunkLengths[ 0 ] = 0;
+
+ if ( ! nSubDivisions ) nSubDivisions = 100;
+
+ nSamples = this.points.length * nSubDivisions;
+
+ oldPosition.copy( this.points[ 0 ] );
+
+ for ( i = 1; i < nSamples; i ++ ) {
+
+ index = i / nSamples;
+
+ position = this.getPoint( index );
+ tmpVec.copy( position );
+
+ totalLength += tmpVec.distanceTo( oldPosition );
+
+ oldPosition.copy( position );
+
+ point = ( this.points.length - 1 ) * index;
+ intPoint = Math.floor( point );
+
+ if ( intPoint !== oldIntPoint ) {
+
+ chunkLengths[ intPoint ] = totalLength;
+ oldIntPoint = intPoint;
+
+ }
+
+ }
+
+ // last point ends with total length
+
+ chunkLengths[ chunkLengths.length ] = totalLength;
+
+ return { chunks: chunkLengths, total: totalLength };
+
+ };
+
+ this.reparametrizeByArcLength = function ( samplingCoef ) {
+
+ var i, j,
+ index, indexCurrent, indexNext,
+ realDistance,
+ sampling, position,
+ newpoints = [],
+ tmpVec = new THREE.Vector3(),
+ sl = this.getLength();
+
+ newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() );
+
+ for ( i = 1; i < this.points.length; i ++ ) {
+
+ //tmpVec.copy( this.points[ i - 1 ] );
+ //linearDistance = tmpVec.distanceTo( this.points[ i ] );
+
+ realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ];
+
+ sampling = Math.ceil( samplingCoef * realDistance / sl.total );
+
+ indexCurrent = ( i - 1 ) / ( this.points.length - 1 );
+ indexNext = i / ( this.points.length - 1 );
+
+ for ( j = 1; j < sampling - 1; j ++ ) {
+
+ index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent );
+
+ position = this.getPoint( index );
+ newpoints.push( tmpVec.copy( position ).clone() );
+
+ }
+
+ newpoints.push( tmpVec.copy( this.points[ i ] ).clone() );
+
+ }
+
+ this.points = newpoints;
+
+ };
+
+ // Catmull-Rom
+
+ function interpolate( p0, p1, p2, p3, t, t2, t3 ) {
+
+ var v0 = ( p2 - p0 ) * 0.5,
+ v1 = ( p3 - p1 ) * 0.5;
+
+ return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
+
+ }
+
+};
+
+// File:src/math/Triangle.js
+
+/**
+ * @author bhouston / http://clara.io
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Triangle = function ( a, b, c ) {
+
+ this.a = ( a !== undefined ) ? a : new THREE.Vector3();
+ this.b = ( b !== undefined ) ? b : new THREE.Vector3();
+ this.c = ( c !== undefined ) ? c : new THREE.Vector3();
+
+};
+
+THREE.Triangle.normal = function () {
+
+ var v0 = new THREE.Vector3();
+
+ return function ( a, b, c, optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ result.subVectors( c, b );
+ v0.subVectors( a, b );
+ result.cross( v0 );
+
+ var resultLengthSq = result.lengthSq();
+ if ( resultLengthSq > 0 ) {
+
+ return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) );
+
+ }
+
+ return result.set( 0, 0, 0 );
+
+ };
+
+}();
+
+// static/instance method to calculate barycentric coordinates
+// based on: http://www.blackpawn.com/texts/pointinpoly/default.html
+THREE.Triangle.barycoordFromPoint = function () {
+
+ var v0 = new THREE.Vector3();
+ var v1 = new THREE.Vector3();
+ var v2 = new THREE.Vector3();
+
+ return function ( point, a, b, c, optionalTarget ) {
+
+ v0.subVectors( c, a );
+ v1.subVectors( b, a );
+ v2.subVectors( point, a );
+
+ 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 denom = ( dot00 * dot11 - dot01 * dot01 );
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ // collinear or singular triangle
+ if ( denom === 0 ) {
+
+ // arbitrary location outside of triangle?
+ // not sure if this is the best idea, maybe should be returning undefined
+ return result.set( - 2, - 1, - 1 );
+
+ }
+
+ var invDenom = 1 / denom;
+ var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
+ var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
+
+ // barycentric coordinates must always sum to 1
+ return result.set( 1 - u - v, v, u );
+
+ };
+
+}();
+
+THREE.Triangle.containsPoint = function () {
+
+ var v1 = new THREE.Vector3();
+
+ return function ( point, a, b, c ) {
+
+ var result = THREE.Triangle.barycoordFromPoint( point, a, b, c, v1 );
+
+ return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 );
+
+ };
+
+}();
+
+THREE.Triangle.prototype = {
+
+ constructor: THREE.Triangle,
+
+ set: function ( a, b, c ) {
+
+ this.a.copy( a );
+ this.b.copy( b );
+ this.c.copy( c );
+
+ return this;
+
+ },
+
+ setFromPointsAndIndices: function ( points, i0, i1, i2 ) {
+
+ this.a.copy( points[ i0 ] );
+ this.b.copy( points[ i1 ] );
+ this.c.copy( points[ i2 ] );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( triangle ) {
+
+ this.a.copy( triangle.a );
+ this.b.copy( triangle.b );
+ this.c.copy( triangle.c );
+
+ return this;
+
+ },
+
+ area: function () {
+
+ var v0 = new THREE.Vector3();
+ var v1 = new THREE.Vector3();
+
+ return function () {
+
+ v0.subVectors( this.c, this.b );
+ v1.subVectors( this.a, this.b );
+
+ return v0.cross( v1 ).length() * 0.5;
+
+ };
+
+ }(),
+
+ midpoint: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 );
+
+ },
+
+ normal: function ( optionalTarget ) {
+
+ return THREE.Triangle.normal( this.a, this.b, this.c, optionalTarget );
+
+ },
+
+ plane: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Plane();
+
+ return result.setFromCoplanarPoints( this.a, this.b, this.c );
+
+ },
+
+ barycoordFromPoint: function ( point, optionalTarget ) {
+
+ return THREE.Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget );
+
+ },
+
+ containsPoint: function ( point ) {
+
+ return THREE.Triangle.containsPoint( point, this.a, this.b, this.c );
+
+ },
+
+ equals: function ( triangle ) {
+
+ return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c );
+
+ }
+
+};
+
+// File:src/core/Channels.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Channels = function () {
+
+ this.mask = 1;
+
+};
+
+THREE.Channels.prototype = {
+
+ constructor: THREE.Channels,
+
+ set: function ( channel ) {
+
+ this.mask = 1 << channel;
+
+ },
+
+ enable: function ( channel ) {
+
+ this.mask |= 1 << channel;
+
+ },
+
+ toggle: function ( channel ) {
+
+ this.mask ^= 1 << channel;
+
+ },
+
+ disable: function ( channel ) {
+
+ this.mask &= ~ ( 1 << channel );
+
+ }
+
+};
+
+// File:src/core/Clock.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Clock = function ( autoStart ) {
+
+ this.autoStart = ( autoStart !== undefined ) ? autoStart : true;
+
+ this.startTime = 0;
+ this.oldTime = 0;
+ this.elapsedTime = 0;
+
+ this.running = false;
+
+};
+
+THREE.Clock.prototype = {
+
+ constructor: THREE.Clock,
+
+ start: function () {
+
+ this.startTime = self.performance.now();
+
+ this.oldTime = this.startTime;
+ this.running = true;
+
+ },
+
+ stop: function () {
+
+ this.getElapsedTime();
+ this.running = false;
+
+ },
+
+ getElapsedTime: function () {
+
+ this.getDelta();
+ return this.elapsedTime;
+
+ },
+
+ getDelta: function () {
+
+ var diff = 0;
+
+ if ( this.autoStart && ! this.running ) {
+
+ this.start();
+
+ }
+
+ if ( this.running ) {
+
+ var newTime = self.performance.now();
+
+ diff = 0.001 * ( newTime - this.oldTime );
+ this.oldTime = newTime;
+
+ this.elapsedTime += diff;
+
+ }
+
+ return diff;
+
+ }
+
+};
+
+// File:src/core/EventDispatcher.js
+
+/**
+ * https://github.com/mrdoob/eventdispatcher.js/
+ */
+
+THREE.EventDispatcher = function () {};
+
+THREE.EventDispatcher.prototype = {
+
+ constructor: THREE.EventDispatcher,
+
+ apply: function ( object ) {
+
+ object.addEventListener = THREE.EventDispatcher.prototype.addEventListener;
+ object.hasEventListener = THREE.EventDispatcher.prototype.hasEventListener;
+ object.removeEventListener = THREE.EventDispatcher.prototype.removeEventListener;
+ object.dispatchEvent = THREE.EventDispatcher.prototype.dispatchEvent;
+
+ },
+
+ addEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) this._listeners = {};
+
+ var listeners = this._listeners;
+
+ if ( listeners[ type ] === undefined ) {
+
+ listeners[ type ] = [];
+
+ }
+
+ if ( listeners[ type ].indexOf( listener ) === - 1 ) {
+
+ listeners[ type ].push( listener );
+
+ }
+
+ },
+
+ hasEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) return false;
+
+ var listeners = this._listeners;
+
+ if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {
+
+ return true;
+
+ }
+
+ return false;
+
+ },
+
+ removeEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) return;
+
+ var listeners = this._listeners;
+ var listenerArray = listeners[ type ];
+
+ if ( listenerArray !== undefined ) {
+
+ var index = listenerArray.indexOf( listener );
+
+ if ( index !== - 1 ) {
+
+ listenerArray.splice( index, 1 );
+
+ }
+
+ }
+
+ },
+
+ dispatchEvent: function ( event ) {
+
+ if ( this._listeners === undefined ) return;
+
+ var listeners = this._listeners;
+ var listenerArray = listeners[ event.type ];
+
+ if ( listenerArray !== undefined ) {
+
+ event.target = this;
+
+ var array = [];
+ var length = listenerArray.length;
+
+ for ( var i = 0; i < length; i ++ ) {
+
+ array[ i ] = listenerArray[ i ];
+
+ }
+
+ for ( var i = 0; i < length; i ++ ) {
+
+ array[ i ].call( this, event );
+
+ }
+
+ }
+
+ }
+
+};
+
+// File:src/core/Raycaster.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author bhouston / http://clara.io/
+ * @author stephomi / http://stephaneginier.com/
+ */
+
+( function ( THREE ) {
+
+ THREE.Raycaster = function ( origin, direction, near, far ) {
+
+ this.ray = new THREE.Ray( origin, direction );
+ // direction is assumed to be normalized (for accurate distance calculations)
+
+ this.near = near || 0;
+ this.far = far || 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 descSort( a, b ) {
+
+ return a.distance - b.distance;
+
+ }
+
+ function intersectObject( object, raycaster, intersects, recursive ) {
+
+ if ( object.visible === false ) return;
+
+ object.raycast( raycaster, intersects );
+
+ if ( recursive === true ) {
+
+ var children = object.children;
+
+ for ( var i = 0, l = children.length; i < l; i ++ ) {
+
+ intersectObject( children[ i ], raycaster, intersects, true );
+
+ }
+
+ }
+
+ }
+
+ //
+
+ THREE.Raycaster.prototype = {
+
+ constructor: THREE.Raycaster,
+
+ linePrecision: 1,
+
+ set: function ( origin, direction ) {
+
+ // direction is assumed to be normalized (for accurate distance calculations)
+
+ this.ray.set( origin, direction );
+
+ },
+
+ setFromCamera: function ( coords, camera ) {
+
+ if ( camera instanceof THREE.PerspectiveCamera ) {
+
+ this.ray.origin.setFromMatrixPosition( camera.matrixWorld );
+ this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize();
+
+ } else if ( camera instanceof THREE.OrthographicCamera ) {
+
+ this.ray.origin.set( coords.x, coords.y, - 1 ).unproject( camera );
+ this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );
+
+ } else {
+
+ console.error( 'THREE.Raycaster: Unsupported camera type.' );
+
+ }
+
+ },
+
+ intersectObject: function ( object, recursive ) {
+
+ var intersects = [];
+
+ intersectObject( object, this, intersects, recursive );
+
+ intersects.sort( descSort );
+
+ return intersects;
+
+ },
+
+ intersectObjects: function ( objects, recursive ) {
+
+ var intersects = [];
+
+ if ( Array.isArray( objects ) === false ) {
+
+ console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' );
+ return intersects;
+
+ }
+
+ for ( var i = 0, l = objects.length; i < l; i ++ ) {
+
+ intersectObject( objects[ i ], this, intersects, recursive );
+
+ }
+
+ intersects.sort( descSort );
+
+ return intersects;
+
+ }
+
+ };
+
+}( THREE ) );
+
+// File:src/core/Object3D.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author elephantatwork / www.elephantatwork.ch
+ */
+
+THREE.Object3D = function () {
+
+ Object.defineProperty( this, 'id', { value: THREE.Object3DIdCount ++ } );
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.name = '';
+ this.type = 'Object3D';
+
+ this.parent = null;
+ this.channels = new THREE.Channels();
+ this.children = [];
+
+ this.up = THREE.Object3D.DefaultUp.clone();
+
+ var position = new THREE.Vector3();
+ var rotation = new THREE.Euler();
+ var quaternion = new THREE.Quaternion();
+ var scale = new THREE.Vector3( 1, 1, 1 );
+
+ function onRotationChange() {
+
+ quaternion.setFromEuler( rotation, false );
+
+ }
+
+ function onQuaternionChange() {
+
+ rotation.setFromQuaternion( quaternion, undefined, false );
+
+ }
+
+ rotation.onChange( onRotationChange );
+ quaternion.onChange( onQuaternionChange );
+
+ Object.defineProperties( this, {
+ position: {
+ enumerable: true,
+ value: position
+ },
+ rotation: {
+ enumerable: true,
+ value: rotation
+ },
+ quaternion: {
+ enumerable: true,
+ value: quaternion
+ },
+ scale: {
+ enumerable: true,
+ value: scale
+ },
+ modelViewMatrix: {
+ value: new THREE.Matrix4()
+ },
+ normalMatrix: {
+ value: new THREE.Matrix3()
+ }
+ } );
+
+ this.rotationAutoUpdate = true;
+
+ this.matrix = new THREE.Matrix4();
+ this.matrixWorld = new THREE.Matrix4();
+
+ this.matrixAutoUpdate = THREE.Object3D.DefaultMatrixAutoUpdate;
+ this.matrixWorldNeedsUpdate = false;
+
+ this.visible = true;
+
+ this.castShadow = false;
+ this.receiveShadow = false;
+
+ this.frustumCulled = true;
+ this.renderOrder = 0;
+
+ this.userData = {};
+
+};
+
+THREE.Object3D.DefaultUp = new THREE.Vector3( 0, 1, 0 );
+THREE.Object3D.DefaultMatrixAutoUpdate = true;
+
+THREE.Object3D.prototype = {
+
+ constructor: THREE.Object3D,
+
+ get eulerOrder () {
+
+ console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' );
+
+ return this.rotation.order;
+
+ },
+
+ set eulerOrder ( value ) {
+
+ console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' );
+
+ this.rotation.order = value;
+
+ },
+
+ get useQuaternion () {
+
+ console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );
+
+ },
+
+ set useQuaternion ( value ) {
+
+ console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );
+
+ },
+
+ set renderDepth ( value ) {
+
+ console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' );
+
+ },
+
+ //
+
+ applyMatrix: function ( matrix ) {
+
+ this.matrix.multiplyMatrices( matrix, this.matrix );
+
+ this.matrix.decompose( this.position, this.quaternion, this.scale );
+
+ },
+
+ setRotationFromAxisAngle: function ( axis, angle ) {
+
+ // assumes axis is normalized
+
+ this.quaternion.setFromAxisAngle( axis, angle );
+
+ },
+
+ setRotationFromEuler: function ( euler ) {
+
+ this.quaternion.setFromEuler( euler, true );
+
+ },
+
+ setRotationFromMatrix: function ( m ) {
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ this.quaternion.setFromRotationMatrix( m );
+
+ },
+
+ setRotationFromQuaternion: function ( q ) {
+
+ // assumes q is normalized
+
+ this.quaternion.copy( q );
+
+ },
+
+ rotateOnAxis: function () {
+
+ // rotate object on axis in object space
+ // axis is assumed to be normalized
+
+ var q1 = new THREE.Quaternion();
+
+ return function ( axis, angle ) {
+
+ q1.setFromAxisAngle( axis, angle );
+
+ this.quaternion.multiply( q1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ rotateX: function () {
+
+ var v1 = new THREE.Vector3( 1, 0, 0 );
+
+ return function ( angle ) {
+
+ return this.rotateOnAxis( v1, angle );
+
+ };
+
+ }(),
+
+ rotateY: function () {
+
+ var v1 = new THREE.Vector3( 0, 1, 0 );
+
+ return function ( angle ) {
+
+ return this.rotateOnAxis( v1, angle );
+
+ };
+
+ }(),
+
+ rotateZ: function () {
+
+ var v1 = new THREE.Vector3( 0, 0, 1 );
+
+ return function ( angle ) {
+
+ return this.rotateOnAxis( v1, angle );
+
+ };
+
+ }(),
+
+ translateOnAxis: function () {
+
+ // translate object by distance along axis in object space
+ // axis is assumed to be normalized
+
+ var v1 = new THREE.Vector3();
+
+ return function ( axis, distance ) {
+
+ v1.copy( axis ).applyQuaternion( this.quaternion );
+
+ this.position.add( v1.multiplyScalar( distance ) );
+
+ return this;
+
+ };
+
+ }(),
+
+ translate: function ( distance, axis ) {
+
+ console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' );
+ return this.translateOnAxis( axis, distance );
+
+ },
+
+ translateX: function () {
+
+ var v1 = new THREE.Vector3( 1, 0, 0 );
+
+ return function ( distance ) {
+
+ return this.translateOnAxis( v1, distance );
+
+ };
+
+ }(),
+
+ translateY: function () {
+
+ var v1 = new THREE.Vector3( 0, 1, 0 );
+
+ return function ( distance ) {
+
+ return this.translateOnAxis( v1, distance );
+
+ };
+
+ }(),
+
+ translateZ: function () {
+
+ var v1 = new THREE.Vector3( 0, 0, 1 );
+
+ return function ( distance ) {
+
+ return this.translateOnAxis( v1, distance );
+
+ };
+
+ }(),
+
+ localToWorld: function ( vector ) {
+
+ return vector.applyMatrix4( this.matrixWorld );
+
+ },
+
+ worldToLocal: function () {
+
+ var m1 = new THREE.Matrix4();
+
+ return function ( vector ) {
+
+ return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) );
+
+ };
+
+ }(),
+
+ lookAt: function () {
+
+ // This routine does not support objects with rotated and/or translated parent(s)
+
+ var m1 = new THREE.Matrix4();
+
+ return function ( vector ) {
+
+ m1.lookAt( vector, this.position, this.up );
+
+ this.quaternion.setFromRotationMatrix( m1 );
+
+ };
+
+ }(),
+
+ add: function ( object ) {
+
+ if ( arguments.length > 1 ) {
+
+ for ( var i = 0; i < arguments.length; i ++ ) {
+
+ this.add( arguments[ i ] );
+
+ }
+
+ return this;
+
+ }
+
+ if ( object === this ) {
+
+ console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object );
+ return this;
+
+ }
+
+ if ( object instanceof THREE.Object3D ) {
+
+ if ( object.parent !== null ) {
+
+ object.parent.remove( object );
+
+ }
+
+ object.parent = this;
+ object.dispatchEvent( { type: 'added' } );
+
+ this.children.push( object );
+
+ } else {
+
+ console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object );
+
+ }
+
+ return this;
+
+ },
+
+ remove: function ( object ) {
+
+ if ( arguments.length > 1 ) {
+
+ for ( var i = 0; i < arguments.length; i ++ ) {
+
+ this.remove( arguments[ i ] );
+
+ }
+
+ }
+
+ var index = this.children.indexOf( object );
+
+ if ( index !== - 1 ) {
+
+ object.parent = null;
+
+ object.dispatchEvent( { type: 'removed' } );
+
+ this.children.splice( index, 1 );
+
+ }
+
+ },
+
+ getChildByName: function ( name ) {
+
+ console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' );
+ return this.getObjectByName( name );
+
+ },
+
+ getObjectById: function ( id ) {
+
+ return this.getObjectByProperty( 'id', id );
+
+ },
+
+ getObjectByName: function ( name ) {
+
+ return this.getObjectByProperty( 'name', name );
+
+ },
+
+ getObjectByProperty: function ( name, value ) {
+
+ if ( this[ name ] === value ) return this;
+
+ for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+ var child = this.children[ i ];
+ var object = child.getObjectByProperty( name, value );
+
+ if ( object !== undefined ) {
+
+ return object;
+
+ }
+
+ }
+
+ return undefined;
+
+ },
+
+ getWorldPosition: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ this.updateMatrixWorld( true );
+
+ return result.setFromMatrixPosition( this.matrixWorld );
+
+ },
+
+ getWorldQuaternion: function () {
+
+ var position = new THREE.Vector3();
+ var scale = new THREE.Vector3();
+
+ return function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Quaternion();
+
+ this.updateMatrixWorld( true );
+
+ this.matrixWorld.decompose( position, result, scale );
+
+ return result;
+
+ };
+
+ }(),
+
+ getWorldRotation: function () {
+
+ var quaternion = new THREE.Quaternion();
+
+ return function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Euler();
+
+ this.getWorldQuaternion( quaternion );
+
+ return result.setFromQuaternion( quaternion, this.rotation.order, false );
+
+ };
+
+ }(),
+
+ getWorldScale: function () {
+
+ var position = new THREE.Vector3();
+ var quaternion = new THREE.Quaternion();
+
+ return function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ this.updateMatrixWorld( true );
+
+ this.matrixWorld.decompose( position, quaternion, result );
+
+ return result;
+
+ };
+
+ }(),
+
+ getWorldDirection: function () {
+
+ var quaternion = new THREE.Quaternion();
+
+ return function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ this.getWorldQuaternion( quaternion );
+
+ return result.set( 0, 0, 1 ).applyQuaternion( quaternion );
+
+ };
+
+ }(),
+
+ raycast: function () {},
+
+ traverse: function ( callback ) {
+
+ callback( this );
+
+ var children = this.children;
+
+ for ( var i = 0, l = children.length; i < l; i ++ ) {
+
+ children[ i ].traverse( callback );
+
+ }
+
+ },
+
+ traverseVisible: function ( callback ) {
+
+ if ( this.visible === false ) return;
+
+ callback( this );
+
+ var children = this.children;
+
+ for ( var i = 0, l = children.length; i < l; i ++ ) {
+
+ children[ i ].traverseVisible( callback );
+
+ }
+
+ },
+
+ traverseAncestors: function ( callback ) {
+
+ var parent = this.parent;
+
+ if ( parent !== null ) {
+
+ callback( parent );
+
+ parent.traverseAncestors( callback );
+
+ }
+
+ },
+
+ updateMatrix: function () {
+
+ this.matrix.compose( this.position, this.quaternion, this.scale );
+
+ this.matrixWorldNeedsUpdate = true;
+
+ },
+
+ updateMatrixWorld: function ( force ) {
+
+ if ( this.matrixAutoUpdate === true ) this.updateMatrix();
+
+ if ( this.matrixWorldNeedsUpdate === true || force === true ) {
+
+ if ( this.parent === null ) {
+
+ this.matrixWorld.copy( this.matrix );
+
+ } else {
+
+ this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
+
+ }
+
+ this.matrixWorldNeedsUpdate = false;
+
+ force = true;
+
+ }
+
+ // update children
+
+ for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+ this.children[ i ].updateMatrixWorld( force );
+
+ }
+
+ },
+
+ toJSON: function ( meta ) {
+
+ var isRootObject = ( meta === undefined );
+
+ var output = {};
+
+ // meta is a hash used to collect geometries, materials.
+ // not providing it implies that this is the root object
+ // being serialized.
+ if ( isRootObject ) {
+
+ // initialize meta obj
+ meta = {
+ geometries: {},
+ materials: {},
+ textures: {},
+ images: {}
+ };
+
+ output.metadata = {
+ version: 4.4,
+ type: 'Object',
+ generator: 'Object3D.toJSON'
+ };
+
+ }
+
+ // standard Object3D serialization
+
+ var object = {};
+
+ object.uuid = this.uuid;
+ object.type = this.type;
+
+ if ( this.name !== '' ) object.name = this.name;
+ if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
+ if ( this.castShadow === true ) object.castShadow = true;
+ if ( this.receiveShadow === true ) object.receiveShadow = true;
+ if ( this.visible === false ) object.visible = false;
+
+ object.matrix = this.matrix.toArray();
+
+ //
+
+ if ( this.geometry !== undefined ) {
+
+ if ( meta.geometries[ this.geometry.uuid ] === undefined ) {
+
+ meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON( meta );
+
+ }
+
+ object.geometry = this.geometry.uuid;
+
+ }
+
+ if ( this.material !== undefined ) {
+
+ if ( meta.materials[ this.material.uuid ] === undefined ) {
+
+ meta.materials[ this.material.uuid ] = this.material.toJSON( meta );
+
+ }
+
+ object.material = this.material.uuid;
+
+ }
+
+ //
+
+ if ( this.children.length > 0 ) {
+
+ object.children = [];
+
+ for ( var i = 0; i < this.children.length; i ++ ) {
+
+ object.children.push( this.children[ i ].toJSON( meta ).object );
+
+ }
+
+ }
+
+ if ( isRootObject ) {
+
+ var geometries = extractFromCache( meta.geometries );
+ var materials = extractFromCache( meta.materials );
+ var textures = extractFromCache( meta.textures );
+ var images = extractFromCache( meta.images );
+
+ if ( geometries.length > 0 ) output.geometries = geometries;
+ if ( materials.length > 0 ) output.materials = materials;
+ if ( textures.length > 0 ) output.textures = textures;
+ if ( images.length > 0 ) output.images = images;
+
+ }
+
+ output.object = object;
+
+ return output;
+
+ // extract data from the cache hash
+ // remove metadata on each item
+ // and return as array
+ function extractFromCache ( cache ) {
+
+ var values = [];
+ for ( var key in cache ) {
+
+ var data = cache[ key ];
+ delete data.metadata;
+ values.push( data );
+
+ }
+ return values;
+
+ }
+
+ },
+
+ clone: function ( recursive ) {
+
+ return new this.constructor().copy( this, recursive );
+
+ },
+
+ copy: function ( source, recursive ) {
+
+ if ( recursive === undefined ) recursive = true;
+
+ this.name = source.name;
+
+ this.up.copy( source.up );
+
+ this.position.copy( source.position );
+ this.quaternion.copy( source.quaternion );
+ this.scale.copy( source.scale );
+
+ this.rotationAutoUpdate = source.rotationAutoUpdate;
+
+ this.matrix.copy( source.matrix );
+ this.matrixWorld.copy( source.matrixWorld );
+
+ this.matrixAutoUpdate = source.matrixAutoUpdate;
+ this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
+
+ this.visible = source.visible;
+
+ this.castShadow = source.castShadow;
+ this.receiveShadow = source.receiveShadow;
+
+ this.frustumCulled = source.frustumCulled;
+ this.renderOrder = source.renderOrder;
+
+ this.userData = JSON.parse( JSON.stringify( source.userData ) );
+
+ if ( recursive === true ) {
+
+ for ( var i = 0; i < source.children.length; i ++ ) {
+
+ var child = source.children[ i ];
+ this.add( child.clone() );
+
+ }
+
+ }
+
+ return this;
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.Object3D.prototype );
+
+THREE.Object3DIdCount = 0;
+
+// File:src/core/Face3.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) {
+
+ this.a = a;
+ this.b = b;
+ this.c = c;
+
+ this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3();
+ this.vertexNormals = Array.isArray( normal ) ? normal : [];
+
+ this.color = color instanceof THREE.Color ? color : new THREE.Color();
+ this.vertexColors = Array.isArray( color ) ? color : [];
+
+ this.materialIndex = materialIndex !== undefined ? materialIndex : 0;
+
+};
+
+THREE.Face3.prototype = {
+
+ constructor: THREE.Face3,
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.a = source.a;
+ this.b = source.b;
+ this.c = source.c;
+
+ this.normal.copy( source.normal );
+ this.color.copy( source.color );
+
+ this.materialIndex = source.materialIndex;
+
+ for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) {
+
+ this.vertexNormals[ i ] = source.vertexNormals[ i ].clone();
+
+ }
+
+ for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) {
+
+ this.vertexColors[ i ] = source.vertexColors[ i ].clone();
+
+ }
+
+ return this;
+
+ }
+
+};
+
+// File:src/core/Face4.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) {
+
+ console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' );
+ return new THREE.Face3( a, b, c, normal, color, materialIndex );
+
+};
+
+// File:src/core/BufferAttribute.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.BufferAttribute = function ( array, itemSize ) {
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.array = array;
+ this.itemSize = itemSize;
+
+ this.dynamic = false;
+ this.updateRange = { offset: 0, count: - 1 };
+
+ this.version = 0;
+
+};
+
+THREE.BufferAttribute.prototype = {
+
+ constructor: THREE.BufferAttribute,
+
+ get length() {
+
+ console.warn( 'THREE.BufferAttribute: .length has been deprecated. Please use .count.' );
+ return this.array.length;
+
+ },
+
+ get count() {
+
+ return this.array.length / this.itemSize;
+
+ },
+
+ set needsUpdate( value ) {
+
+ if ( value === true ) this.version ++;
+
+ },
+
+ setDynamic: function ( value ) {
+
+ this.dynamic = value;
+
+ return this;
+
+ },
+
+ copy: function ( source ) {
+
+ this.array = new source.array.constructor( source.array );
+ this.itemSize = source.itemSize;
+
+ this.dynamic = source.dynamic;
+
+ return this;
+
+ },
+
+ copyAt: function ( index1, attribute, index2 ) {
+
+ index1 *= this.itemSize;
+ index2 *= attribute.itemSize;
+
+ for ( var i = 0, l = this.itemSize; i < l; i ++ ) {
+
+ this.array[ index1 + i ] = attribute.array[ index2 + i ];
+
+ }
+
+ return this;
+
+ },
+
+ copyArray: function ( array ) {
+
+ this.array.set( array );
+
+ return this;
+
+ },
+
+ copyColorsArray: function ( colors ) {
+
+ var array = this.array, offset = 0;
+
+ for ( var i = 0, l = colors.length; i < l; i ++ ) {
+
+ var color = colors[ i ];
+
+ if ( color === undefined ) {
+
+ console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i );
+ color = new THREE.Color();
+
+ }
+
+ array[ offset ++ ] = color.r;
+ array[ offset ++ ] = color.g;
+ array[ offset ++ ] = color.b;
+
+ }
+
+ return this;
+
+ },
+
+ copyIndicesArray: function ( indices ) {
+
+ var array = this.array, offset = 0;
+
+ for ( var i = 0, l = indices.length; i < l; i ++ ) {
+
+ var index = indices[ i ];
+
+ array[ offset ++ ] = index.a;
+ array[ offset ++ ] = index.b;
+ array[ offset ++ ] = index.c;
+
+ }
+
+ return this;
+
+ },
+
+ copyVector2sArray: function ( vectors ) {
+
+ var array = this.array, offset = 0;
+
+ for ( var i = 0, l = vectors.length; i < l; i ++ ) {
+
+ var vector = vectors[ i ];
+
+ if ( vector === undefined ) {
+
+ console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i );
+ vector = new THREE.Vector2();
+
+ }
+
+ array[ offset ++ ] = vector.x;
+ array[ offset ++ ] = vector.y;
+
+ }
+
+ return this;
+
+ },
+
+ copyVector3sArray: function ( vectors ) {
+
+ var array = this.array, offset = 0;
+
+ for ( var i = 0, l = vectors.length; i < l; i ++ ) {
+
+ var vector = vectors[ i ];
+
+ if ( vector === undefined ) {
+
+ console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i );
+ vector = new THREE.Vector3();
+
+ }
+
+ array[ offset ++ ] = vector.x;
+ array[ offset ++ ] = vector.y;
+ array[ offset ++ ] = vector.z;
+
+ }
+
+ return this;
+
+ },
+
+ copyVector4sArray: function ( vectors ) {
+
+ var array = this.array, offset = 0;
+
+ for ( var i = 0, l = vectors.length; i < l; i ++ ) {
+
+ var vector = vectors[ i ];
+
+ if ( vector === undefined ) {
+
+ console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i );
+ vector = new THREE.Vector4();
+
+ }
+
+ array[ offset ++ ] = vector.x;
+ array[ offset ++ ] = vector.y;
+ array[ offset ++ ] = vector.z;
+ array[ offset ++ ] = vector.w;
+
+ }
+
+ return this;
+
+ },
+
+ set: function ( value, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.array.set( value, offset );
+
+ return this;
+
+ },
+
+ getX: function ( index ) {
+
+ return this.array[ index * this.itemSize ];
+
+ },
+
+ setX: function ( index, x ) {
+
+ this.array[ index * this.itemSize ] = x;
+
+ return this;
+
+ },
+
+ getY: function ( index ) {
+
+ return this.array[ index * this.itemSize + 1 ];
+
+ },
+
+ setY: function ( index, y ) {
+
+ this.array[ index * this.itemSize + 1 ] = y;
+
+ return this;
+
+ },
+
+ getZ: function ( index ) {
+
+ return this.array[ index * this.itemSize + 2 ];
+
+ },
+
+ setZ: function ( index, z ) {
+
+ this.array[ index * this.itemSize + 2 ] = z;
+
+ return this;
+
+ },
+
+ getW: function ( index ) {
+
+ return this.array[ index * this.itemSize + 3 ];
+
+ },
+
+ setW: function ( index, w ) {
+
+ this.array[ index * this.itemSize + 3 ] = w;
+
+ return this;
+
+ },
+
+ setXY: function ( index, x, y ) {
+
+ index *= this.itemSize;
+
+ this.array[ index + 0 ] = x;
+ this.array[ index + 1 ] = y;
+
+ return this;
+
+ },
+
+ setXYZ: function ( index, x, y, z ) {
+
+ index *= this.itemSize;
+
+ this.array[ index + 0 ] = x;
+ this.array[ index + 1 ] = y;
+ this.array[ index + 2 ] = z;
+
+ return this;
+
+ },
+
+ setXYZW: function ( index, x, y, z, w ) {
+
+ index *= this.itemSize;
+
+ this.array[ index + 0 ] = x;
+ this.array[ index + 1 ] = y;
+ this.array[ index + 2 ] = z;
+ this.array[ index + 3 ] = w;
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ }
+
+};
+
+//
+
+THREE.Int8Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Int8Array( array ), itemSize );
+
+};
+
+THREE.Uint8Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Uint8Array( array ), itemSize );
+
+};
+
+THREE.Uint8ClampedAttribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Uint8ClampedArray( array ), itemSize );
+
+};
+
+THREE.Int16Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Int16Array( array ), itemSize );
+
+};
+
+THREE.Uint16Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Uint16Array( array ), itemSize );
+
+};
+
+THREE.Int32Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Int32Array( array ), itemSize );
+
+};
+
+THREE.Uint32Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Uint32Array( array ), itemSize );
+
+};
+
+THREE.Float32Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Float32Array( array ), itemSize );
+
+};
+
+THREE.Float64Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Float64Array( array ), itemSize );
+
+};
+
+
+// Deprecated
+
+THREE.DynamicBufferAttribute = function ( array, itemSize ) {
+
+ console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' );
+ return new THREE.BufferAttribute( array, itemSize ).setDynamic( true );
+
+};
+
+// File:src/core/InstancedBufferAttribute.js
+
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InstancedBufferAttribute = function ( array, itemSize, meshPerAttribute ) {
+
+ THREE.BufferAttribute.call( this, array, itemSize );
+
+ this.meshPerAttribute = meshPerAttribute || 1;
+
+};
+
+THREE.InstancedBufferAttribute.prototype = Object.create( THREE.BufferAttribute.prototype );
+THREE.InstancedBufferAttribute.prototype.constructor = THREE.InstancedBufferAttribute;
+
+THREE.InstancedBufferAttribute.prototype.copy = function ( source ) {
+
+ THREE.BufferAttribute.prototype.copy.call( this, source );
+
+ this.meshPerAttribute = source.meshPerAttribute;
+
+ return this;
+
+};
+
+// File:src/core/InterleavedBuffer.js
+
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InterleavedBuffer = function ( array, stride ) {
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.array = array;
+ this.stride = stride;
+
+ this.dynamic = false;
+ this.updateRange = { offset: 0, count: - 1 };
+
+ this.version = 0;
+
+};
+
+THREE.InterleavedBuffer.prototype = {
+
+ constructor: THREE.InterleavedBuffer,
+
+ get length () {
+
+ return this.array.length;
+
+ },
+
+ get count () {
+
+ return this.array.length / this.stride;
+
+ },
+
+ set needsUpdate( value ) {
+
+ if ( value === true ) this.version ++;
+
+ },
+
+ setDynamic: function ( value ) {
+
+ this.dynamic = value;
+
+ return this;
+
+ },
+
+ copy: function ( source ) {
+
+ this.array = new source.array.constructor( source.array );
+ this.stride = source.stride;
+ this.dynamic = source.dynamic;
+
+ },
+
+ copyAt: function ( index1, attribute, index2 ) {
+
+ index1 *= this.stride;
+ index2 *= attribute.stride;
+
+ for ( var i = 0, l = this.stride; i < l; i ++ ) {
+
+ this.array[ index1 + i ] = attribute.array[ index2 + i ];
+
+ }
+
+ return this;
+
+ },
+
+ set: function ( value, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.array.set( value, offset );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ }
+
+};
+
+// File:src/core/InstancedInterleavedBuffer.js
+
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InstancedInterleavedBuffer = function ( array, stride, meshPerAttribute ) {
+
+ THREE.InterleavedBuffer.call( this, array, stride );
+
+ this.meshPerAttribute = meshPerAttribute || 1;
+
+};
+
+THREE.InstancedInterleavedBuffer.prototype = Object.create( THREE.InterleavedBuffer.prototype );
+THREE.InstancedInterleavedBuffer.prototype.constructor = THREE.InstancedInterleavedBuffer;
+
+THREE.InstancedInterleavedBuffer.prototype.copy = function ( source ) {
+
+ THREE.InterleavedBuffer.prototype.copy.call( this, source );
+
+ this.meshPerAttribute = source.meshPerAttribute;
+
+ return this;
+
+};
+
+// File:src/core/InterleavedBufferAttribute.js
+
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InterleavedBufferAttribute = function ( interleavedBuffer, itemSize, offset ) {
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.data = interleavedBuffer;
+ this.itemSize = itemSize;
+ this.offset = offset;
+
+};
+
+
+THREE.InterleavedBufferAttribute.prototype = {
+
+ constructor: THREE.InterleavedBufferAttribute,
+
+ get length() {
+
+ console.warn( 'THREE.BufferAttribute: .length has been deprecated. Please use .count.' );
+ return this.array.length;
+
+ },
+
+ get count() {
+
+ return this.data.array.length / this.data.stride;
+
+ },
+
+ setX: function ( index, x ) {
+
+ this.data.array[ index * this.data.stride + this.offset ] = x;
+
+ return this;
+
+ },
+
+ setY: function ( index, y ) {
+
+ this.data.array[ index * this.data.stride + this.offset + 1 ] = y;
+
+ return this;
+
+ },
+
+ setZ: function ( index, z ) {
+
+ this.data.array[ index * this.data.stride + this.offset + 2 ] = z;
+
+ return this;
+
+ },
+
+ setW: function ( index, w ) {
+
+ this.data.array[ index * this.data.stride + this.offset + 3 ] = w;
+
+ return this;
+
+ },
+
+ getX: function ( index ) {
+
+ return this.data.array[ index * this.data.stride + this.offset ];
+
+ },
+
+ getY: function ( index ) {
+
+ return this.data.array[ index * this.data.stride + this.offset + 1 ];
+
+ },
+
+ getZ: function ( index ) {
+
+ return this.data.array[ index * this.data.stride + this.offset + 2 ];
+
+ },
+
+ getW: function ( index ) {
+
+ return this.data.array[ index * this.data.stride + this.offset + 3 ];
+
+ },
+
+ setXY: function ( index, x, y ) {
+
+ index = index * this.data.stride + this.offset;
+
+ this.data.array[ index + 0 ] = x;
+ this.data.array[ index + 1 ] = y;
+
+ return this;
+
+ },
+
+ setXYZ: function ( index, x, y, z ) {
+
+ index = index * this.data.stride + this.offset;
+
+ this.data.array[ index + 0 ] = x;
+ this.data.array[ index + 1 ] = y;
+ this.data.array[ index + 2 ] = z;
+
+ return this;
+
+ },
+
+ setXYZW: function ( index, x, y, z, w ) {
+
+ index = index * this.data.stride + this.offset;
+
+ this.data.array[ index + 0 ] = x;
+ this.data.array[ index + 1 ] = y;
+ this.data.array[ index + 2 ] = z;
+ this.data.array[ index + 3 ] = w;
+
+ return this;
+
+ }
+
+};
+
+// File:src/core/Geometry.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author kile / http://kile.stravaganza.org/
+ * @author alteredq / http://alteredqualia.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Geometry = function () {
+
+ Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } );
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.name = '';
+ this.type = 'Geometry';
+
+ this.vertices = [];
+ this.colors = [];
+ this.faces = [];
+ this.faceVertexUvs = [ [] ];
+
+ this.morphTargets = [];
+ this.morphNormals = [];
+
+ this.skinWeights = [];
+ this.skinIndices = [];
+
+ this.lineDistances = [];
+
+ this.boundingBox = null;
+ this.boundingSphere = null;
+
+ // update flags
+
+ this.verticesNeedUpdate = false;
+ this.elementsNeedUpdate = false;
+ this.uvsNeedUpdate = false;
+ this.normalsNeedUpdate = false;
+ this.colorsNeedUpdate = false;
+ this.lineDistancesNeedUpdate = false;
+ this.groupsNeedUpdate = false;
+
+};
+
+THREE.Geometry.prototype = {
+
+ constructor: THREE.Geometry,
+
+ applyMatrix: function ( matrix ) {
+
+ var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );
+
+ for ( var i = 0, il = this.vertices.length; i < il; i ++ ) {
+
+ var vertex = this.vertices[ i ];
+ vertex.applyMatrix4( matrix );
+
+ }
+
+ for ( var i = 0, il = this.faces.length; i < il; i ++ ) {
+
+ var face = this.faces[ i ];
+ face.normal.applyMatrix3( normalMatrix ).normalize();
+
+ for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
+
+ face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize();
+
+ }
+
+ }
+
+ if ( this.boundingBox !== null ) {
+
+ this.computeBoundingBox();
+
+ }
+
+ if ( this.boundingSphere !== null ) {
+
+ this.computeBoundingSphere();
+
+ }
+
+ this.verticesNeedUpdate = true;
+ this.normalsNeedUpdate = true;
+
+ },
+
+ rotateX: function () {
+
+ // rotate geometry around world x-axis
+
+ var m1;
+
+ return function rotateX( angle ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeRotationX( angle );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ rotateY: function () {
+
+ // rotate geometry around world y-axis
+
+ var m1;
+
+ return function rotateY( angle ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeRotationY( angle );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ rotateZ: function () {
+
+ // rotate geometry around world z-axis
+
+ var m1;
+
+ return function rotateZ( angle ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeRotationZ( angle );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ translate: function () {
+
+ // translate geometry
+
+ var m1;
+
+ return function translate( x, y, z ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeTranslation( x, y, z );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ scale: function () {
+
+ // scale geometry
+
+ var m1;
+
+ return function scale( x, y, z ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeScale( x, y, z );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ lookAt: function () {
+
+ var obj;
+
+ return function lookAt( vector ) {
+
+ if ( obj === undefined ) obj = new THREE.Object3D();
+
+ obj.lookAt( vector );
+
+ obj.updateMatrix();
+
+ this.applyMatrix( obj.matrix );
+
+ };
+
+ }(),
+
+ fromBufferGeometry: function ( geometry ) {
+
+ var scope = this;
+
+ var indices = geometry.index !== null ? geometry.index.array : undefined;
+ var attributes = geometry.attributes;
+
+ var vertices = attributes.position.array;
+ var normals = attributes.normal !== undefined ? attributes.normal.array : undefined;
+ var colors = attributes.color !== undefined ? attributes.color.array : undefined;
+ var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined;
+ var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined;
+
+ if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = [];
+
+ var tempNormals = [];
+ var tempUVs = [];
+ var tempUVs2 = [];
+
+ for ( var i = 0, j = 0, k = 0; i < vertices.length; i += 3, j += 2, k += 4 ) {
+
+ scope.vertices.push( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) );
+
+ if ( normals !== undefined ) {
+
+ tempNormals.push( new THREE.Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) );
+
+ }
+
+ if ( colors !== undefined ) {
+
+ scope.colors.push( new THREE.Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) );
+
+ }
+
+ if ( uvs !== undefined ) {
+
+ tempUVs.push( new THREE.Vector2( uvs[ j ], uvs[ j + 1 ] ) );
+
+ }
+
+ if ( uvs2 !== undefined ) {
+
+ tempUVs2.push( new THREE.Vector2( uvs2[ j ], uvs2[ j + 1 ] ) );
+
+ }
+
+ }
+
+ function addFace( a, b, c ) {
+
+ var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : [];
+ var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : [];
+
+ var face = new THREE.Face3( a, b, c, vertexNormals, vertexColors );
+
+ scope.faces.push( face );
+
+ if ( uvs !== undefined ) {
+
+ scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] );
+
+ }
+
+ if ( uvs2 !== undefined ) {
+
+ scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] );
+
+ }
+
+ };
+
+ if ( indices !== undefined ) {
+
+ var groups = geometry.groups;
+
+ if ( groups.length > 0 ) {
+
+ for ( var i = 0; i < groups.length; i ++ ) {
+
+ var group = groups[ i ];
+
+ var start = group.start;
+ var count = group.count;
+
+ for ( var j = start, jl = start + count; j < jl; j += 3 ) {
+
+ addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ] );
+
+ }
+
+ }
+
+ } else {
+
+ for ( var i = 0; i < indices.length; i += 3 ) {
+
+ addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );
+
+ }
+
+ }
+
+ } else {
+
+ for ( var i = 0; i < vertices.length / 3; i += 3 ) {
+
+ addFace( i, i + 1, i + 2 );
+
+ }
+
+ }
+
+ this.computeFaceNormals();
+
+ if ( geometry.boundingBox !== null ) {
+
+ this.boundingBox = geometry.boundingBox.clone();
+
+ }
+
+ if ( geometry.boundingSphere !== null ) {
+
+ this.boundingSphere = geometry.boundingSphere.clone();
+
+ }
+
+ return this;
+
+ },
+
+ center: function () {
+
+ this.computeBoundingBox();
+
+ var offset = this.boundingBox.center().negate();
+
+ this.translate( offset.x, offset.y, offset.z );
+
+ return offset;
+
+ },
+
+ normalize: function () {
+
+ this.computeBoundingSphere();
+
+ var center = this.boundingSphere.center;
+ var radius = this.boundingSphere.radius;
+
+ var s = radius === 0 ? 1 : 1.0 / radius;
+
+ var matrix = new THREE.Matrix4();
+ matrix.set(
+ s, 0, 0, - s * center.x,
+ 0, s, 0, - s * center.y,
+ 0, 0, s, - s * center.z,
+ 0, 0, 0, 1
+ );
+
+ this.applyMatrix( matrix );
+
+ return this;
+
+ },
+
+ computeFaceNormals: function () {
+
+ var cb = new THREE.Vector3(), ab = new THREE.Vector3();
+
+ for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ var face = this.faces[ f ];
+
+ var vA = this.vertices[ face.a ];
+ var vB = this.vertices[ face.b ];
+ var vC = this.vertices[ face.c ];
+
+ cb.subVectors( vC, vB );
+ ab.subVectors( vA, vB );
+ cb.cross( ab );
+
+ cb.normalize();
+
+ face.normal.copy( cb );
+
+ }
+
+ },
+
+ computeVertexNormals: function ( areaWeighted ) {
+
+ var v, vl, f, fl, face, vertices;
+
+ vertices = new Array( this.vertices.length );
+
+ for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
+
+ vertices[ v ] = new THREE.Vector3();
+
+ }
+
+ if ( areaWeighted ) {
+
+ // vertex normals weighted by triangle areas
+ // http://www.iquilezles.org/www/articles/normals/normals.htm
+
+ var vA, vB, vC;
+ var cb = new THREE.Vector3(), ab = new THREE.Vector3();
+
+ for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ face = this.faces[ f ];
+
+ vA = this.vertices[ face.a ];
+ vB = this.vertices[ face.b ];
+ vC = this.vertices[ face.c ];
+
+ cb.subVectors( vC, vB );
+ ab.subVectors( vA, vB );
+ cb.cross( ab );
+
+ vertices[ face.a ].add( cb );
+ vertices[ face.b ].add( cb );
+ vertices[ face.c ].add( cb );
+
+ }
+
+ } else {
+
+ for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ face = this.faces[ f ];
+
+ vertices[ face.a ].add( face.normal );
+ vertices[ face.b ].add( face.normal );
+ vertices[ face.c ].add( face.normal );
+
+ }
+
+ }
+
+ for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
+
+ vertices[ v ].normalize();
+
+ }
+
+ for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ face = this.faces[ f ];
+
+ var vertexNormals = face.vertexNormals;
+
+ if ( vertexNormals.length === 3 ) {
+
+ vertexNormals[ 0 ].copy( vertices[ face.a ] );
+ vertexNormals[ 1 ].copy( vertices[ face.b ] );
+ vertexNormals[ 2 ].copy( vertices[ face.c ] );
+
+ } else {
+
+ vertexNormals[ 0 ] = vertices[ face.a ].clone();
+ vertexNormals[ 1 ] = vertices[ face.b ].clone();
+ vertexNormals[ 2 ] = vertices[ face.c ].clone();
+
+ }
+
+ }
+
+ },
+
+ computeMorphNormals: function () {
+
+ var i, il, f, fl, face;
+
+ // save original normals
+ // - create temp variables on first access
+ // otherwise just copy (for faster repeated calls)
+
+ for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ face = this.faces[ f ];
+
+ if ( ! face.__originalFaceNormal ) {
+
+ face.__originalFaceNormal = face.normal.clone();
+
+ } else {
+
+ face.__originalFaceNormal.copy( face.normal );
+
+ }
+
+ if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = [];
+
+ for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) {
+
+ if ( ! face.__originalVertexNormals[ i ] ) {
+
+ face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone();
+
+ } else {
+
+ face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] );
+
+ }
+
+ }
+
+ }
+
+ // use temp geometry to compute face and vertex normals for each morph
+
+ var tmpGeo = new THREE.Geometry();
+ tmpGeo.faces = this.faces;
+
+ for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) {
+
+ // create on first access
+
+ if ( ! this.morphNormals[ i ] ) {
+
+ this.morphNormals[ i ] = {};
+ this.morphNormals[ i ].faceNormals = [];
+ this.morphNormals[ i ].vertexNormals = [];
+
+ var dstNormalsFace = this.morphNormals[ i ].faceNormals;
+ var dstNormalsVertex = this.morphNormals[ i ].vertexNormals;
+
+ var faceNormal, vertexNormals;
+
+ for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ faceNormal = new THREE.Vector3();
+ vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() };
+
+ dstNormalsFace.push( faceNormal );
+ dstNormalsVertex.push( vertexNormals );
+
+ }
+
+ }
+
+ var morphNormals = this.morphNormals[ i ];
+
+ // set vertices to morph target
+
+ tmpGeo.vertices = this.morphTargets[ i ].vertices;
+
+ // compute morph normals
+
+ tmpGeo.computeFaceNormals();
+ tmpGeo.computeVertexNormals();
+
+ // store morph normals
+
+ var faceNormal, vertexNormals;
+
+ for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ face = this.faces[ f ];
+
+ faceNormal = morphNormals.faceNormals[ f ];
+ vertexNormals = morphNormals.vertexNormals[ f ];
+
+ faceNormal.copy( face.normal );
+
+ vertexNormals.a.copy( face.vertexNormals[ 0 ] );
+ vertexNormals.b.copy( face.vertexNormals[ 1 ] );
+ vertexNormals.c.copy( face.vertexNormals[ 2 ] );
+
+ }
+
+ }
+
+ // restore original normals
+
+ for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ face = this.faces[ f ];
+
+ face.normal = face.__originalFaceNormal;
+ face.vertexNormals = face.__originalVertexNormals;
+
+ }
+
+ },
+
+ computeTangents: function () {
+
+ console.warn( 'THREE.Geometry: .computeTangents() has been removed.' );
+
+ },
+
+ computeLineDistances: function () {
+
+ var d = 0;
+ var vertices = this.vertices;
+
+ for ( var i = 0, il = vertices.length; i < il; i ++ ) {
+
+ if ( i > 0 ) {
+
+ d += vertices[ i ].distanceTo( vertices[ i - 1 ] );
+
+ }
+
+ this.lineDistances[ i ] = d;
+
+ }
+
+ },
+
+ computeBoundingBox: function () {
+
+ if ( this.boundingBox === null ) {
+
+ this.boundingBox = new THREE.Box3();
+
+ }
+
+ this.boundingBox.setFromPoints( this.vertices );
+
+ },
+
+ computeBoundingSphere: function () {
+
+ if ( this.boundingSphere === null ) {
+
+ this.boundingSphere = new THREE.Sphere();
+
+ }
+
+ this.boundingSphere.setFromPoints( this.vertices );
+
+ },
+
+ merge: function ( geometry, matrix, materialIndexOffset ) {
+
+ if ( geometry instanceof THREE.Geometry === false ) {
+
+ console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry );
+ return;
+
+ }
+
+ var normalMatrix,
+ vertexOffset = this.vertices.length,
+ vertices1 = this.vertices,
+ vertices2 = geometry.vertices,
+ faces1 = this.faces,
+ faces2 = geometry.faces,
+ uvs1 = this.faceVertexUvs[ 0 ],
+ uvs2 = geometry.faceVertexUvs[ 0 ];
+
+ if ( materialIndexOffset === undefined ) materialIndexOffset = 0;
+
+ if ( matrix !== undefined ) {
+
+ normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );
+
+ }
+
+ // vertices
+
+ for ( var i = 0, il = vertices2.length; i < il; i ++ ) {
+
+ var vertex = vertices2[ i ];
+
+ var vertexCopy = vertex.clone();
+
+ if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix );
+
+ vertices1.push( vertexCopy );
+
+ }
+
+ // faces
+
+ for ( i = 0, il = faces2.length; i < il; i ++ ) {
+
+ var face = faces2[ i ], faceCopy, normal, color,
+ faceVertexNormals = face.vertexNormals,
+ faceVertexColors = face.vertexColors;
+
+ faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset );
+ faceCopy.normal.copy( face.normal );
+
+ if ( normalMatrix !== undefined ) {
+
+ faceCopy.normal.applyMatrix3( normalMatrix ).normalize();
+
+ }
+
+ for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) {
+
+ normal = faceVertexNormals[ j ].clone();
+
+ if ( normalMatrix !== undefined ) {
+
+ normal.applyMatrix3( normalMatrix ).normalize();
+
+ }
+
+ faceCopy.vertexNormals.push( normal );
+
+ }
+
+ faceCopy.color.copy( face.color );
+
+ for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) {
+
+ color = faceVertexColors[ j ];
+ faceCopy.vertexColors.push( color.clone() );
+
+ }
+
+ faceCopy.materialIndex = face.materialIndex + materialIndexOffset;
+
+ faces1.push( faceCopy );
+
+ }
+
+ // uvs
+
+ for ( i = 0, il = uvs2.length; i < il; i ++ ) {
+
+ var uv = uvs2[ i ], uvCopy = [];
+
+ if ( uv === undefined ) {
+
+ continue;
+
+ }
+
+ for ( var j = 0, jl = uv.length; j < jl; j ++ ) {
+
+ uvCopy.push( uv[ j ].clone() );
+
+ }
+
+ uvs1.push( uvCopy );
+
+ }
+
+ },
+
+ mergeMesh: function ( mesh ) {
+
+ if ( mesh instanceof THREE.Mesh === false ) {
+
+ console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh );
+ return;
+
+ }
+
+ mesh.matrixAutoUpdate && mesh.updateMatrix();
+
+ this.merge( mesh.geometry, mesh.matrix );
+
+ },
+
+ /*
+ * Checks for duplicate vertices with hashmap.
+ * Duplicated vertices are removed
+ * and faces' vertices are updated.
+ */
+
+ mergeVertices: function () {
+
+ var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)
+ var unique = [], changes = [];
+
+ var v, key;
+ var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001
+ var precision = Math.pow( 10, precisionPoints );
+ var i, il, face;
+ var indices, j, jl;
+
+ for ( i = 0, il = this.vertices.length; i < il; i ++ ) {
+
+ v = this.vertices[ i ];
+ key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision );
+
+ if ( verticesMap[ key ] === undefined ) {
+
+ verticesMap[ key ] = i;
+ unique.push( this.vertices[ i ] );
+ changes[ i ] = unique.length - 1;
+
+ } else {
+
+ //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]);
+ changes[ i ] = changes[ verticesMap[ key ] ];
+
+ }
+
+ }
+
+
+ // if faces are completely degenerate after merging vertices, we
+ // have to remove them from the geometry.
+ var faceIndicesToRemove = [];
+
+ for ( i = 0, il = this.faces.length; i < il; i ++ ) {
+
+ face = this.faces[ i ];
+
+ face.a = changes[ face.a ];
+ face.b = changes[ face.b ];
+ face.c = changes[ face.c ];
+
+ indices = [ face.a, face.b, face.c ];
+
+ var dupIndex = - 1;
+
+ // if any duplicate vertices are found in a Face3
+ // we have to remove the face as nothing can be saved
+ for ( var n = 0; n < 3; n ++ ) {
+
+ if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) {
+
+ dupIndex = n;
+ faceIndicesToRemove.push( i );
+ break;
+
+ }
+
+ }
+
+ }
+
+ for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) {
+
+ var idx = faceIndicesToRemove[ i ];
+
+ this.faces.splice( idx, 1 );
+
+ for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {
+
+ this.faceVertexUvs[ j ].splice( idx, 1 );
+
+ }
+
+ }
+
+ // Use unique set of vertices
+
+ var diff = this.vertices.length - unique.length;
+ this.vertices = unique;
+ return diff;
+
+ },
+
+ sortFacesByMaterialIndex: function () {
+
+ var faces = this.faces;
+ var length = faces.length;
+
+ // tag faces
+
+ for ( var i = 0; i < length; i ++ ) {
+
+ faces[ i ]._id = i;
+
+ }
+
+ // sort faces
+
+ function materialIndexSort( a, b ) {
+
+ return a.materialIndex - b.materialIndex;
+
+ }
+
+ faces.sort( materialIndexSort );
+
+ // sort uvs
+
+ var uvs1 = this.faceVertexUvs[ 0 ];
+ var uvs2 = this.faceVertexUvs[ 1 ];
+
+ var newUvs1, newUvs2;
+
+ if ( uvs1 && uvs1.length === length ) newUvs1 = [];
+ if ( uvs2 && uvs2.length === length ) newUvs2 = [];
+
+ for ( var i = 0; i < length; i ++ ) {
+
+ var id = faces[ i ]._id;
+
+ if ( newUvs1 ) newUvs1.push( uvs1[ id ] );
+ if ( newUvs2 ) newUvs2.push( uvs2[ id ] );
+
+ }
+
+ if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1;
+ if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2;
+
+ },
+
+ toJSON: function () {
+
+ var data = {
+ metadata: {
+ version: 4.4,
+ type: 'Geometry',
+ generator: 'Geometry.toJSON'
+ }
+ };
+
+ // standard Geometry serialization
+
+ data.uuid = this.uuid;
+ data.type = this.type;
+ if ( this.name !== '' ) data.name = this.name;
+
+ if ( this.parameters !== undefined ) {
+
+ var parameters = this.parameters;
+
+ for ( var key in parameters ) {
+
+ if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];
+
+ }
+
+ return data;
+
+ }
+
+ var vertices = [];
+
+ for ( var i = 0; i < this.vertices.length; i ++ ) {
+
+ var vertex = this.vertices[ i ];
+ vertices.push( vertex.x, vertex.y, vertex.z );
+
+ }
+
+ var faces = [];
+ var normals = [];
+ var normalsHash = {};
+ var colors = [];
+ var colorsHash = {};
+ var uvs = [];
+ var uvsHash = {};
+
+ for ( var i = 0; i < this.faces.length; i ++ ) {
+
+ var face = this.faces[ i ];
+
+ var hasMaterial = false; // face.materialIndex !== undefined;
+ var hasFaceUv = false; // deprecated
+ var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined;
+ var hasFaceNormal = face.normal.length() > 0;
+ var hasFaceVertexNormal = face.vertexNormals.length > 0;
+ var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1;
+ var hasFaceVertexColor = face.vertexColors.length > 0;
+
+ var faceType = 0;
+
+ faceType = setBit( faceType, 0, 0 );
+ faceType = setBit( faceType, 1, hasMaterial );
+ faceType = setBit( faceType, 2, hasFaceUv );
+ faceType = setBit( faceType, 3, hasFaceVertexUv );
+ faceType = setBit( faceType, 4, hasFaceNormal );
+ faceType = setBit( faceType, 5, hasFaceVertexNormal );
+ faceType = setBit( faceType, 6, hasFaceColor );
+ faceType = setBit( faceType, 7, hasFaceVertexColor );
+
+ faces.push( faceType );
+ faces.push( face.a, face.b, face.c );
+
+ if ( hasFaceVertexUv ) {
+
+ var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ];
+
+ faces.push(
+ getUvIndex( faceVertexUvs[ 0 ] ),
+ getUvIndex( faceVertexUvs[ 1 ] ),
+ getUvIndex( faceVertexUvs[ 2 ] )
+ );
+
+ }
+
+ if ( hasFaceNormal ) {
+
+ faces.push( getNormalIndex( face.normal ) );
+
+ }
+
+ if ( hasFaceVertexNormal ) {
+
+ var vertexNormals = face.vertexNormals;
+
+ faces.push(
+ getNormalIndex( vertexNormals[ 0 ] ),
+ getNormalIndex( vertexNormals[ 1 ] ),
+ getNormalIndex( vertexNormals[ 2 ] )
+ );
+
+ }
+
+ if ( hasFaceColor ) {
+
+ faces.push( getColorIndex( face.color ) );
+
+ }
+
+ if ( hasFaceVertexColor ) {
+
+ var vertexColors = face.vertexColors;
+
+ faces.push(
+ getColorIndex( vertexColors[ 0 ] ),
+ getColorIndex( vertexColors[ 1 ] ),
+ getColorIndex( vertexColors[ 2 ] )
+ );
+
+ }
+
+ }
+
+ function setBit( value, position, enabled ) {
+
+ return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) );
+
+ }
+
+ function getNormalIndex( normal ) {
+
+ var hash = normal.x.toString() + normal.y.toString() + normal.z.toString();
+
+ if ( normalsHash[ hash ] !== undefined ) {
+
+ return normalsHash[ hash ];
+
+ }
+
+ normalsHash[ hash ] = normals.length / 3;
+ normals.push( normal.x, normal.y, normal.z );
+
+ return normalsHash[ hash ];
+
+ }
+
+ function getColorIndex( color ) {
+
+ var hash = color.r.toString() + color.g.toString() + color.b.toString();
+
+ if ( colorsHash[ hash ] !== undefined ) {
+
+ return colorsHash[ hash ];
+
+ }
+
+ colorsHash[ hash ] = colors.length;
+ colors.push( color.getHex() );
+
+ return colorsHash[ hash ];
+
+ }
+
+ function getUvIndex( uv ) {
+
+ var hash = uv.x.toString() + uv.y.toString();
+
+ if ( uvsHash[ hash ] !== undefined ) {
+
+ return uvsHash[ hash ];
+
+ }
+
+ uvsHash[ hash ] = uvs.length / 2;
+ uvs.push( uv.x, uv.y );
+
+ return uvsHash[ hash ];
+
+ }
+
+ data.data = {};
+
+ data.data.vertices = vertices;
+ data.data.normals = normals;
+ if ( colors.length > 0 ) data.data.colors = colors;
+ if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility
+ data.data.faces = faces;
+
+ return data;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.vertices = [];
+ this.faces = [];
+ this.faceVertexUvs = [ [] ];
+
+ var vertices = source.vertices;
+
+ for ( var i = 0, il = vertices.length; i < il; i ++ ) {
+
+ this.vertices.push( vertices[ i ].clone() );
+
+ }
+
+ var faces = source.faces;
+
+ for ( var i = 0, il = faces.length; i < il; i ++ ) {
+
+ this.faces.push( faces[ i ].clone() );
+
+ }
+
+ for ( var i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) {
+
+ var faceVertexUvs = source.faceVertexUvs[ i ];
+
+ if ( this.faceVertexUvs[ i ] === undefined ) {
+
+ this.faceVertexUvs[ i ] = [];
+
+ }
+
+ for ( var j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) {
+
+ var uvs = faceVertexUvs[ j ], uvsCopy = [];
+
+ for ( var k = 0, kl = uvs.length; k < kl; k ++ ) {
+
+ var uv = uvs[ k ];
+
+ uvsCopy.push( uv.clone() );
+
+ }
+
+ this.faceVertexUvs[ i ].push( uvsCopy );
+
+ }
+
+ }
+
+ return this;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.Geometry.prototype );
+
+THREE.GeometryIdCount = 0;
+
+// File:src/core/DirectGeometry.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.DirectGeometry = function () {
+
+ Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } );
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.name = '';
+ this.type = 'DirectGeometry';
+
+ this.indices = [];
+ this.vertices = [];
+ this.normals = [];
+ this.colors = [];
+ this.uvs = [];
+ this.uvs2 = [];
+
+ this.groups = [];
+
+ this.morphTargets = {};
+
+ this.skinWeights = [];
+ this.skinIndices = [];
+
+ // this.lineDistances = [];
+
+ this.boundingBox = null;
+ this.boundingSphere = null;
+
+ // update flags
+
+ this.verticesNeedUpdate = false;
+ this.normalsNeedUpdate = false;
+ this.colorsNeedUpdate = false;
+ this.uvsNeedUpdate = false;
+ this.groupsNeedUpdate = false;
+
+};
+
+THREE.DirectGeometry.prototype = {
+
+ constructor: THREE.DirectGeometry,
+
+ computeBoundingBox: THREE.Geometry.prototype.computeBoundingBox,
+ computeBoundingSphere: THREE.Geometry.prototype.computeBoundingSphere,
+
+ computeFaceNormals: function () {
+
+ console.warn( 'THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.' );
+
+ },
+
+ computeVertexNormals: function () {
+
+ console.warn( 'THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.' );
+
+ },
+
+ computeGroups: function ( geometry ) {
+
+ var group;
+ var groups = [];
+ var materialIndex;
+
+ var faces = geometry.faces;
+
+ for ( var i = 0; i < faces.length; i ++ ) {
+
+ var face = faces[ i ];
+
+ // materials
+
+ if ( face.materialIndex !== materialIndex ) {
+
+ materialIndex = face.materialIndex;
+
+ if ( group !== undefined ) {
+
+ group.count = ( i * 3 ) - group.start;
+ groups.push( group );
+
+ }
+
+ group = {
+ start: i * 3,
+ materialIndex: materialIndex
+ };
+
+ }
+
+ }
+
+ if ( group !== undefined ) {
+
+ group.count = ( i * 3 ) - group.start;
+ groups.push( group );
+
+ }
+
+ this.groups = groups;
+
+ },
+
+ fromGeometry: function ( geometry ) {
+
+ var faces = geometry.faces;
+ var vertices = geometry.vertices;
+ var faceVertexUvs = geometry.faceVertexUvs;
+
+ var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0;
+ var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0;
+
+ // morphs
+
+ var morphTargets = geometry.morphTargets;
+ var morphTargetsLength = morphTargets.length;
+
+ if ( morphTargetsLength > 0 ) {
+
+ var morphTargetsPosition = [];
+
+ for ( var i = 0; i < morphTargetsLength; i ++ ) {
+
+ morphTargetsPosition[ i ] = [];
+
+ }
+
+ this.morphTargets.position = morphTargetsPosition;
+
+ }
+
+ var morphNormals = geometry.morphNormals;
+ var morphNormalsLength = morphNormals.length;
+
+ if ( morphNormalsLength > 0 ) {
+
+ var morphTargetsNormal = [];
+
+ for ( var i = 0; i < morphNormalsLength; i ++ ) {
+
+ morphTargetsNormal[ i ] = [];
+
+ }
+
+ this.morphTargets.normal = morphTargetsNormal;
+
+ }
+
+ // skins
+
+ var skinIndices = geometry.skinIndices;
+ var skinWeights = geometry.skinWeights;
+
+ var hasSkinIndices = skinIndices.length === vertices.length;
+ var hasSkinWeights = skinWeights.length === vertices.length;
+
+ //
+
+ for ( var i = 0; i < faces.length; i ++ ) {
+
+ var face = faces[ i ];
+
+ this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] );
+
+ var vertexNormals = face.vertexNormals;
+
+ if ( vertexNormals.length === 3 ) {
+
+ this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] );
+
+ } else {
+
+ var normal = face.normal;
+
+ this.normals.push( normal, normal, normal );
+
+ }
+
+ var vertexColors = face.vertexColors;
+
+ if ( vertexColors.length === 3 ) {
+
+ this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] );
+
+ } else {
+
+ var color = face.color;
+
+ this.colors.push( color, color, color );
+
+ }
+
+ if ( hasFaceVertexUv === true ) {
+
+ var vertexUvs = faceVertexUvs[ 0 ][ i ];
+
+ if ( vertexUvs !== undefined ) {
+
+ this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );
+
+ } else {
+
+ console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i );
+
+ this.uvs.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() );
+
+ }
+
+ }
+
+ if ( hasFaceVertexUv2 === true ) {
+
+ var vertexUvs = faceVertexUvs[ 1 ][ i ];
+
+ if ( vertexUvs !== undefined ) {
+
+ this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );
+
+ } else {
+
+ console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i );
+
+ this.uvs2.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() );
+
+ }
+
+ }
+
+ // morphs
+
+ for ( var j = 0; j < morphTargetsLength; j ++ ) {
+
+ var morphTarget = morphTargets[ j ].vertices;
+
+ morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] );
+
+ }
+
+ for ( var j = 0; j < morphNormalsLength; j ++ ) {
+
+ var morphNormal = morphNormals[ j ].vertexNormals[ i ];
+
+ morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c );
+
+ }
+
+ // skins
+
+ if ( hasSkinIndices ) {
+
+ this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] );
+
+ }
+
+ if ( hasSkinWeights ) {
+
+ this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] );
+
+ }
+
+ }
+
+ this.computeGroups( geometry );
+
+ this.verticesNeedUpdate = geometry.verticesNeedUpdate;
+ this.normalsNeedUpdate = geometry.normalsNeedUpdate;
+ this.colorsNeedUpdate = geometry.colorsNeedUpdate;
+ this.uvsNeedUpdate = geometry.uvsNeedUpdate;
+ this.groupsNeedUpdate = geometry.groupsNeedUpdate;
+
+ return this;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.DirectGeometry.prototype );
+
+// File:src/core/BufferGeometry.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.BufferGeometry = function () {
+
+ Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } );
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.name = '';
+ this.type = 'BufferGeometry';
+
+ this.index = null;
+ this.attributes = {};
+
+ this.morphAttributes = {};
+
+ this.groups = [];
+
+ this.boundingBox = null;
+ this.boundingSphere = null;
+
+ this.drawRange = { start: 0, count: Infinity };
+
+};
+
+THREE.BufferGeometry.prototype = {
+
+ constructor: THREE.BufferGeometry,
+
+ addIndex: function ( index ) {
+
+ console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' );
+ this.setIndex( index );
+
+ },
+
+ getIndex: function () {
+
+ return this.index;
+
+ },
+
+ setIndex: function ( index ) {
+
+ this.index = index;
+
+ },
+
+ addAttribute: function ( name, attribute ) {
+
+ if ( attribute instanceof THREE.BufferAttribute === false && attribute instanceof THREE.InterleavedBufferAttribute === false ) {
+
+ console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' );
+
+ this.addAttribute( name, new THREE.BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) );
+
+ return;
+
+ }
+
+ if ( name === 'index' ) {
+
+ console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' );
+ this.setIndex( attribute );
+
+ return;
+
+ }
+
+ this.attributes[ name ] = attribute;
+
+ },
+
+ getAttribute: function ( name ) {
+
+ return this.attributes[ name ];
+
+ },
+
+ removeAttribute: function ( name ) {
+
+ delete this.attributes[ name ];
+
+ },
+
+ get drawcalls() {
+
+ console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' );
+ return this.groups;
+
+ },
+
+ get offsets() {
+
+ console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' );
+ return this.groups;
+
+ },
+
+ addDrawCall: function ( start, count, indexOffset ) {
+
+ if ( indexOffset !== undefined ) {
+
+ console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' );
+
+ }
+
+ console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' );
+ this.addGroup( start, count );
+
+ },
+
+ clearDrawCalls: function () {
+
+ console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' );
+ this.clearGroups();
+
+ },
+
+ addGroup: function ( start, count, materialIndex ) {
+
+ this.groups.push( {
+
+ start: start,
+ count: count,
+ materialIndex: materialIndex !== undefined ? materialIndex : 0
+
+ } );
+
+ },
+
+ clearGroups: function () {
+
+ this.groups = [];
+
+ },
+
+ setDrawRange: function ( start, count ) {
+
+ this.drawRange.start = start;
+ this.drawRange.count = count;
+
+ },
+
+ applyMatrix: function ( matrix ) {
+
+ var position = this.attributes.position;
+
+ if ( position !== undefined ) {
+
+ matrix.applyToVector3Array( position.array );
+ position.needsUpdate = true;
+
+ }
+
+ var normal = this.attributes.normal;
+
+ if ( normal !== undefined ) {
+
+ var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );
+
+ normalMatrix.applyToVector3Array( normal.array );
+ normal.needsUpdate = true;
+
+ }
+
+ if ( this.boundingBox !== null ) {
+
+ this.computeBoundingBox();
+
+ }
+
+ if ( this.boundingSphere !== null ) {
+
+ this.computeBoundingSphere();
+
+ }
+
+ },
+
+ rotateX: function () {
+
+ // rotate geometry around world x-axis
+
+ var m1;
+
+ return function rotateX( angle ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeRotationX( angle );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ rotateY: function () {
+
+ // rotate geometry around world y-axis
+
+ var m1;
+
+ return function rotateY( angle ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeRotationY( angle );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ rotateZ: function () {
+
+ // rotate geometry around world z-axis
+
+ var m1;
+
+ return function rotateZ( angle ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeRotationZ( angle );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ translate: function () {
+
+ // translate geometry
+
+ var m1;
+
+ return function translate( x, y, z ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeTranslation( x, y, z );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ scale: function () {
+
+ // scale geometry
+
+ var m1;
+
+ return function scale( x, y, z ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeScale( x, y, z );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ lookAt: function () {
+
+ var obj;
+
+ return function lookAt( vector ) {
+
+ if ( obj === undefined ) obj = new THREE.Object3D();
+
+ obj.lookAt( vector );
+
+ obj.updateMatrix();
+
+ this.applyMatrix( obj.matrix );
+
+ };
+
+ }(),
+
+ center: function () {
+
+ this.computeBoundingBox();
+
+ var offset = this.boundingBox.center().negate();
+
+ this.translate( offset.x, offset.y, offset.z );
+
+ return offset;
+
+ },
+
+ setFromObject: function ( object ) {
+
+ // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this );
+
+ var geometry = object.geometry;
+
+ if ( object instanceof THREE.Points || object instanceof THREE.Line ) {
+
+ var positions = new THREE.Float32Attribute( geometry.vertices.length * 3, 3 );
+ var colors = new THREE.Float32Attribute( geometry.colors.length * 3, 3 );
+
+ this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) );
+ this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) );
+
+ if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) {
+
+ var lineDistances = new THREE.Float32Attribute( geometry.lineDistances.length, 1 );
+
+ this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) );
+
+ }
+
+ if ( geometry.boundingSphere !== null ) {
+
+ this.boundingSphere = geometry.boundingSphere.clone();
+
+ }
+
+ if ( geometry.boundingBox !== null ) {
+
+ this.boundingBox = geometry.boundingBox.clone();
+
+ }
+
+ } else if ( object instanceof THREE.Mesh ) {
+
+ if ( geometry instanceof THREE.Geometry ) {
+
+ this.fromGeometry( geometry );
+
+ }
+
+ }
+
+ return this;
+
+ },
+
+ updateFromObject: function ( object ) {
+
+ var geometry = object.geometry;
+
+ if ( object instanceof THREE.Mesh ) {
+
+ var direct = geometry.__directGeometry;
+
+ if ( direct === undefined ) {
+
+ return this.fromGeometry( geometry );
+
+ }
+
+ direct.verticesNeedUpdate = geometry.verticesNeedUpdate;
+ direct.normalsNeedUpdate = geometry.normalsNeedUpdate;
+ direct.colorsNeedUpdate = geometry.colorsNeedUpdate;
+ direct.uvsNeedUpdate = geometry.uvsNeedUpdate;
+ direct.groupsNeedUpdate = geometry.groupsNeedUpdate;
+
+ geometry.verticesNeedUpdate = false;
+ geometry.normalsNeedUpdate = false;
+ geometry.colorsNeedUpdate = false;
+ geometry.uvsNeedUpdate = false;
+ geometry.groupsNeedUpdate = false;
+
+ geometry = direct;
+
+ }
+
+ if ( geometry.verticesNeedUpdate === true ) {
+
+ var attribute = this.attributes.position;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyVector3sArray( geometry.vertices );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.verticesNeedUpdate = false;
+
+ }
+
+ if ( geometry.normalsNeedUpdate === true ) {
+
+ var attribute = this.attributes.normal;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyVector3sArray( geometry.normals );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.normalsNeedUpdate = false;
+
+ }
+
+ if ( geometry.colorsNeedUpdate === true ) {
+
+ var attribute = this.attributes.color;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyColorsArray( geometry.colors );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.colorsNeedUpdate = false;
+
+ }
+
+ if ( geometry.uvsNeedUpdate ) {
+
+ var attribute = this.attributes.uv;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyVector2sArray( geometry.uvs );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.uvsNeedUpdate = false;
+
+ }
+
+ if ( geometry.lineDistancesNeedUpdate ) {
+
+ var attribute = this.attributes.lineDistance;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyArray( geometry.lineDistances );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.lineDistancesNeedUpdate = false;
+
+ }
+
+ if ( geometry.groupsNeedUpdate ) {
+
+ geometry.computeGroups( object.geometry );
+ this.groups = geometry.groups;
+
+ geometry.groupsNeedUpdate = false;
+
+ }
+
+ return this;
+
+ },
+
+ fromGeometry: function ( geometry ) {
+
+ geometry.__directGeometry = new THREE.DirectGeometry().fromGeometry( geometry );
+
+ return this.fromDirectGeometry( geometry.__directGeometry );
+
+ },
+
+ fromDirectGeometry: function ( geometry ) {
+
+ var positions = new Float32Array( geometry.vertices.length * 3 );
+ this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) );
+
+ if ( geometry.normals.length > 0 ) {
+
+ var normals = new Float32Array( geometry.normals.length * 3 );
+ this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) );
+
+ }
+
+ if ( geometry.colors.length > 0 ) {
+
+ var colors = new Float32Array( geometry.colors.length * 3 );
+ this.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) );
+
+ }
+
+ if ( geometry.uvs.length > 0 ) {
+
+ var uvs = new Float32Array( geometry.uvs.length * 2 );
+ this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) );
+
+ }
+
+ if ( geometry.uvs2.length > 0 ) {
+
+ var uvs2 = new Float32Array( geometry.uvs2.length * 2 );
+ this.addAttribute( 'uv2', new THREE.BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) );
+
+ }
+
+ if ( geometry.indices.length > 0 ) {
+
+ var TypeArray = geometry.vertices.length > 65535 ? Uint32Array : Uint16Array;
+ var indices = new TypeArray( geometry.indices.length * 3 );
+ this.setIndex( new THREE.BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices ) );
+
+ }
+
+ // groups
+
+ this.groups = geometry.groups;
+
+ // morphs
+
+ for ( var name in geometry.morphTargets ) {
+
+ var array = [];
+ var morphTargets = geometry.morphTargets[ name ];
+
+ for ( var i = 0, l = morphTargets.length; i < l; i ++ ) {
+
+ var morphTarget = morphTargets[ i ];
+
+ var attribute = new THREE.Float32Attribute( morphTarget.length * 3, 3 );
+
+ array.push( attribute.copyVector3sArray( morphTarget ) );
+
+ }
+
+ this.morphAttributes[ name ] = array;
+
+ }
+
+ // skinning
+
+ if ( geometry.skinIndices.length > 0 ) {
+
+ var skinIndices = new THREE.Float32Attribute( geometry.skinIndices.length * 4, 4 );
+ this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) );
+
+ }
+
+ if ( geometry.skinWeights.length > 0 ) {
+
+ var skinWeights = new THREE.Float32Attribute( geometry.skinWeights.length * 4, 4 );
+ this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) );
+
+ }
+
+ //
+
+ if ( geometry.boundingSphere !== null ) {
+
+ this.boundingSphere = geometry.boundingSphere.clone();
+
+ }
+
+ if ( geometry.boundingBox !== null ) {
+
+ this.boundingBox = geometry.boundingBox.clone();
+
+ }
+
+ return this;
+
+ },
+
+ computeBoundingBox: function () {
+
+ var vector = new THREE.Vector3();
+
+ return function () {
+
+ if ( this.boundingBox === null ) {
+
+ this.boundingBox = new THREE.Box3();
+
+ }
+
+ var positions = this.attributes.position.array;
+
+ if ( positions ) {
+
+ var bb = this.boundingBox;
+ bb.makeEmpty();
+
+ for ( var i = 0, il = positions.length; i < il; i += 3 ) {
+
+ vector.fromArray( positions, i );
+ bb.expandByPoint( vector );
+
+ }
+
+ }
+
+ if ( positions === undefined || positions.length === 0 ) {
+
+ this.boundingBox.min.set( 0, 0, 0 );
+ this.boundingBox.max.set( 0, 0, 0 );
+
+ }
+
+ if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {
+
+ console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this );
+
+ }
+
+ };
+
+ }(),
+
+ computeBoundingSphere: function () {
+
+ var box = new THREE.Box3();
+ var vector = new THREE.Vector3();
+
+ return function () {
+
+ if ( this.boundingSphere === null ) {
+
+ this.boundingSphere = new THREE.Sphere();
+
+ }
+
+ var positions = this.attributes.position.array;
+
+ if ( positions ) {
+
+ box.makeEmpty();
+
+ var center = this.boundingSphere.center;
+
+ for ( var i = 0, il = positions.length; i < il; i += 3 ) {
+
+ vector.fromArray( positions, i );
+ box.expandByPoint( vector );
+
+ }
+
+ box.center( center );
+
+ // hoping to find a boundingSphere with a radius smaller than the
+ // boundingSphere of the boundingBox: sqrt(3) smaller in the best case
+
+ var maxRadiusSq = 0;
+
+ for ( var i = 0, il = positions.length; i < il; i += 3 ) {
+
+ vector.fromArray( positions, i );
+ maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) );
+
+ }
+
+ this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
+
+ if ( isNaN( this.boundingSphere.radius ) ) {
+
+ console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this );
+
+ }
+
+ }
+
+ };
+
+ }(),
+
+ computeFaceNormals: function () {
+
+ // backwards compatibility
+
+ },
+
+ computeVertexNormals: function () {
+
+ var index = this.index;
+ var attributes = this.attributes;
+ var groups = this.groups;
+
+ if ( attributes.position ) {
+
+ var positions = attributes.position.array;
+
+ if ( attributes.normal === undefined ) {
+
+ this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( positions.length ), 3 ) );
+
+ } else {
+
+ // reset existing normals to zero
+
+ var normals = attributes.normal.array;
+
+ for ( var i = 0, il = normals.length; i < il; i ++ ) {
+
+ normals[ i ] = 0;
+
+ }
+
+ }
+
+ var normals = attributes.normal.array;
+
+ var vA, vB, vC,
+
+ pA = new THREE.Vector3(),
+ pB = new THREE.Vector3(),
+ pC = new THREE.Vector3(),
+
+ cb = new THREE.Vector3(),
+ ab = new THREE.Vector3();
+
+ // indexed elements
+
+ if ( index ) {
+
+ var indices = index.array;
+
+ if ( groups.length === 0 ) {
+
+ this.addGroup( 0, indices.length );
+
+ }
+
+ for ( var j = 0, jl = groups.length; j < jl; ++ j ) {
+
+ var group = groups[ j ];
+
+ var start = group.start;
+ var count = group.count;
+
+ for ( var i = start, il = start + count; i < il; i += 3 ) {
+
+ vA = indices[ i + 0 ] * 3;
+ vB = indices[ i + 1 ] * 3;
+ vC = indices[ i + 2 ] * 3;
+
+ pA.fromArray( positions, vA );
+ pB.fromArray( positions, vB );
+ pC.fromArray( positions, vC );
+
+ cb.subVectors( pC, pB );
+ ab.subVectors( pA, pB );
+ cb.cross( ab );
+
+ normals[ vA ] += cb.x;
+ normals[ vA + 1 ] += cb.y;
+ normals[ vA + 2 ] += cb.z;
+
+ normals[ vB ] += cb.x;
+ normals[ vB + 1 ] += cb.y;
+ normals[ vB + 2 ] += cb.z;
+
+ normals[ vC ] += cb.x;
+ normals[ vC + 1 ] += cb.y;
+ normals[ vC + 2 ] += cb.z;
+
+ }
+
+ }
+
+ } else {
+
+ // non-indexed elements (unconnected triangle soup)
+
+ for ( var i = 0, il = positions.length; i < il; i += 9 ) {
+
+ pA.fromArray( positions, i );
+ pB.fromArray( positions, i + 3 );
+ pC.fromArray( positions, i + 6 );
+
+ cb.subVectors( pC, pB );
+ ab.subVectors( pA, pB );
+ cb.cross( ab );
+
+ normals[ i ] = cb.x;
+ normals[ i + 1 ] = cb.y;
+ normals[ i + 2 ] = cb.z;
+
+ normals[ i + 3 ] = cb.x;
+ normals[ i + 4 ] = cb.y;
+ normals[ i + 5 ] = cb.z;
+
+ normals[ i + 6 ] = cb.x;
+ normals[ i + 7 ] = cb.y;
+ normals[ i + 8 ] = cb.z;
+
+ }
+
+ }
+
+ this.normalizeNormals();
+
+ attributes.normal.needsUpdate = true;
+
+ }
+
+ },
+
+ computeTangents: function () {
+
+ console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' );
+
+ },
+
+ computeOffsets: function ( size ) {
+
+ console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.')
+
+ },
+
+ merge: function ( geometry, offset ) {
+
+ if ( geometry instanceof THREE.BufferGeometry === false ) {
+
+ console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry );
+ return;
+
+ }
+
+ if ( offset === undefined ) offset = 0;
+
+ var attributes = this.attributes;
+
+ for ( var key in attributes ) {
+
+ if ( geometry.attributes[ key ] === undefined ) continue;
+
+ var attribute1 = attributes[ key ];
+ var attributeArray1 = attribute1.array;
+
+ var attribute2 = geometry.attributes[ key ];
+ var attributeArray2 = attribute2.array;
+
+ var attributeSize = attribute2.itemSize;
+
+ for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) {
+
+ attributeArray1[ j ] = attributeArray2[ i ];
+
+ }
+
+ }
+
+ return this;
+
+ },
+
+ normalizeNormals: function () {
+
+ var normals = this.attributes.normal.array;
+
+ var x, y, z, n;
+
+ for ( var i = 0, il = normals.length; i < il; i += 3 ) {
+
+ x = normals[ i ];
+ y = normals[ i + 1 ];
+ z = normals[ i + 2 ];
+
+ n = 1.0 / Math.sqrt( x * x + y * y + z * z );
+
+ normals[ i ] *= n;
+ normals[ i + 1 ] *= n;
+ normals[ i + 2 ] *= n;
+
+ }
+
+ },
+
+ toJSON: function () {
+
+ var data = {
+ metadata: {
+ version: 4.4,
+ type: 'BufferGeometry',
+ generator: 'BufferGeometry.toJSON'
+ }
+ };
+
+ // standard BufferGeometry serialization
+
+ data.uuid = this.uuid;
+ data.type = this.type;
+ if ( this.name !== '' ) data.name = this.name;
+
+ if ( this.parameters !== undefined ) {
+
+ var parameters = this.parameters;
+
+ for ( var key in parameters ) {
+
+ if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];
+
+ }
+
+ return data;
+
+ }
+
+ data.data = { attributes: {} };
+
+ var index = this.index;
+
+ if ( index !== null ) {
+
+ var array = Array.prototype.slice.call( index.array );
+
+ data.data.index = {
+ type: index.array.constructor.name,
+ array: array
+ };
+
+ }
+
+ var attributes = this.attributes;
+
+ for ( var key in attributes ) {
+
+ var attribute = attributes[ key ];
+
+ var array = Array.prototype.slice.call( attribute.array );
+
+ data.data.attributes[ key ] = {
+ itemSize: attribute.itemSize,
+ type: attribute.array.constructor.name,
+ array: array
+ };
+
+ }
+
+ var groups = this.groups;
+
+ if ( groups.length > 0 ) {
+
+ data.data.groups = JSON.parse( JSON.stringify( groups ) );
+
+ }
+
+ var boundingSphere = this.boundingSphere;
+
+ if ( boundingSphere !== null ) {
+
+ data.data.boundingSphere = {
+ center: boundingSphere.center.toArray(),
+ radius: boundingSphere.radius
+ };
+
+ }
+
+ return data;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ var index = source.index;
+
+ if ( index !== null ) {
+
+ this.setIndex( index.clone() );
+
+ }
+
+ var attributes = source.attributes;
+
+ for ( var name in attributes ) {
+
+ var attribute = attributes[ name ];
+ this.addAttribute( name, attribute.clone() );
+
+ }
+
+ var groups = source.groups;
+
+ for ( var i = 0, l = groups.length; i < l; i ++ ) {
+
+ var group = groups[ i ];
+ this.addGroup( group.start, group.count );
+
+ }
+
+ return this;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype );
+
+THREE.BufferGeometry.MaxIndex = 65535;
+
+// File:src/core/InstancedBufferGeometry.js
+
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InstancedBufferGeometry = function () {
+
+ THREE.BufferGeometry.call( this );
+
+ this.type = 'InstancedBufferGeometry';
+ this.maxInstancedCount = undefined;
+
+};
+
+THREE.InstancedBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.InstancedBufferGeometry.prototype.constructor = THREE.InstancedBufferGeometry;
+
+THREE.InstancedBufferGeometry.prototype.addGroup = function ( start, count, instances ) {
+
+ this.groups.push( {
+
+ start: start,
+ count: count,
+ instances: instances
+
+ } );
+
+};
+
+THREE.InstancedBufferGeometry.prototype.copy = function ( source ) {
+
+ var index = source.index;
+
+ if ( index !== null ) {
+
+ this.setIndex( index.clone() );
+
+ }
+
+ var attributes = source.attributes;
+
+ for ( var name in attributes ) {
+
+ var attribute = attributes[ name ];
+ this.addAttribute( name, attribute.clone() );
+
+ }
+
+ var groups = source.groups;
+
+ for ( var i = 0, l = groups.length; i < l; i ++ ) {
+
+ var group = groups[ i ];
+ this.addGroup( group.start, group.count, group.instances );
+
+ }
+
+ return this;
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.InstancedBufferGeometry.prototype );
+
+// File:src/animation/AnimationAction.js
+
+/**
+ *
+ * A clip that has been explicitly scheduled.
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.AnimationAction = function ( clip, startTime, timeScale, weight, loop ) {
+
+ if ( clip === undefined ) throw new Error( 'clip is null' );
+ this.clip = clip;
+ this.localRoot = null;
+ this.startTime = startTime || 0;
+ this.timeScale = timeScale || 1;
+ this.weight = weight || 1;
+ this.loop = loop || THREE.LoopRepeat;
+ this.loopCount = 0;
+ this.enabled = true; // allow for easy disabling of the action.
+
+ this.actionTime = - this.startTime;
+ this.clipTime = 0;
+
+ this.propertyBindings = [];
+};
+
+/*
+THREE.LoopOnce = 2200;
+THREE.LoopRepeat = 2201;
+THREE.LoopPingPing = 2202;
+*/
+
+THREE.AnimationAction.prototype = {
+
+ constructor: THREE.AnimationAction,
+
+ setLocalRoot: function( localRoot ) {
+
+ this.localRoot = localRoot;
+
+ return this;
+
+ },
+
+ updateTime: function( clipDeltaTime ) {
+
+ var previousClipTime = this.clipTime;
+ var previousLoopCount = this.loopCount;
+ var previousActionTime = this.actionTime;
+
+ var duration = this.clip.duration;
+
+ this.actionTime = this.actionTime + clipDeltaTime;
+
+ if ( this.loop === THREE.LoopOnce ) {
+
+ this.loopCount = 0;
+ this.clipTime = Math.min( Math.max( this.actionTime, 0 ), duration );
+
+ // if time is changed since last time, see if we have hit a start/end limit
+ if ( this.clipTime !== previousClipTime ) {
+
+ if ( this.clipTime === duration ) {
+
+ this.mixer.dispatchEvent( { type: 'finished', action: this, direction: 1 } );
+
+ } else if ( this.clipTime === 0 ) {
+
+ this.mixer.dispatchEvent( { type: 'finished', action: this, direction: -1 } );
+
+ }
+
+ }
+
+
+ return this.clipTime;
+
+ }
+
+ this.loopCount = Math.floor( this.actionTime / duration );
+
+ var newClipTime = this.actionTime - this.loopCount * duration;
+ newClipTime = newClipTime % duration;
+
+ // if we are ping pong looping, ensure that we go backwards when appropriate
+ if ( this.loop == THREE.LoopPingPong ) {
+
+ if ( Math.abs( this.loopCount % 2 ) === 1 ) {
+
+ newClipTime = duration - newClipTime;
+
+ }
+
+ }
+
+ this.clipTime = newClipTime;
+
+ if ( this.loopCount !== previousLoopCount ) {
+
+ this.mixer.dispatchEvent( { type: 'loop', action: this, loopDelta: ( this.loopCount - this.loopCount ) } );
+
+ }
+
+ return this.clipTime;
+
+ },
+
+ syncWith: function( action ) {
+
+ this.actionTime = action.actionTime;
+ this.timeScale = action.timeScale;
+
+ return this;
+ },
+
+ warpToDuration: function( duration ) {
+
+ this.timeScale = this.clip.duration / duration;
+
+ return this;
+ },
+
+ init: function( time ) {
+
+ this.clipTime = time - this.startTime;
+
+ return this;
+
+ },
+
+ update: function( clipDeltaTime ) {
+
+ this.updateTime( clipDeltaTime );
+
+ var clipResults = this.clip.getAt( this.clipTime );
+
+ return clipResults;
+
+ },
+
+ getTimeScaleAt: function( time ) {
+
+ if ( this.timeScale.getAt ) {
+ // pass in time, not clip time, allows for fadein/fadeout across multiple loops of the clip
+ return this.timeScale.getAt( time );
+
+ }
+
+ return this.timeScale;
+
+ },
+
+ getWeightAt: function( time ) {
+
+ if ( this.weight.getAt ) {
+ // pass in time, not clip time, allows for fadein/fadeout across multiple loops of the clip
+ return this.weight.getAt( time );
+
+ }
+
+ return this.weight;
+
+ }
+
+};
+
+// File:src/animation/AnimationClip.js
+
+/**
+ *
+ * Reusable set of Tracks that represent an animation.
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.AnimationClip = function ( name, duration, tracks ) {
+
+ this.name = name;
+ this.tracks = tracks;
+ this.duration = ( duration !== undefined ) ? duration : -1;
+
+ // this means it should figure out its duration by scanning the tracks
+ if ( this.duration < 0 ) {
+ for ( var i = 0; i < this.tracks.length; i ++ ) {
+ var track = this.tracks[i];
+ this.duration = Math.max( track.keys[ track.keys.length - 1 ].time );
+ }
+ }
+
+ // maybe only do these on demand, as doing them here could potentially slow down loading
+ // but leaving these here during development as this ensures a lot of testing of these functions
+ this.trim();
+ this.optimize();
+
+ this.results = [];
+
+};
+
+THREE.AnimationClip.prototype = {
+
+ constructor: THREE.AnimationClip,
+
+ getAt: function( clipTime ) {
+
+ clipTime = Math.max( 0, Math.min( clipTime, this.duration ) );
+
+ for ( var i = 0; i < this.tracks.length; i ++ ) {
+
+ var track = this.tracks[ i ];
+
+ this.results[ i ] = track.getAt( clipTime );
+
+ }
+
+ return this.results;
+ },
+
+ trim: function() {
+
+ for ( var i = 0; i < this.tracks.length; i ++ ) {
+
+ this.tracks[ i ].trim( 0, this.duration );
+
+ }
+
+ return this;
+
+ },
+
+ optimize: function() {
+
+ for ( var i = 0; i < this.tracks.length; i ++ ) {
+
+ this.tracks[ i ].optimize();
+
+ }
+
+ return this;
+
+ }
+
+};
+
+
+THREE.AnimationClip.CreateFromMorphTargetSequence = function( name, morphTargetSequence, fps ) {
+
+
+ var numMorphTargets = morphTargetSequence.length;
+ var tracks = [];
+
+ for ( var i = 0; i < numMorphTargets; i ++ ) {
+
+ var keys = [];
+
+ keys.push( { time: ( i + numMorphTargets - 1 ) % numMorphTargets, value: 0 } );
+ keys.push( { time: i, value: 1 } );
+ keys.push( { time: ( i + 1 ) % numMorphTargets, value: 0 } );
+
+ keys.sort( THREE.KeyframeTrack.keyComparer );
+
+ // if there is a key at the first frame, duplicate it as the last frame as well for perfect loop.
+ if ( keys[0].time === 0 ) {
+ keys.push( {
+ time: numMorphTargets,
+ value: keys[0].value
+ });
+ }
+
+ tracks.push( new THREE.NumberKeyframeTrack( '.morphTargetInfluences[' + morphTargetSequence[i].name + ']', keys ).scale( 1.0 / fps ) );
+ }
+
+ return new THREE.AnimationClip( name, -1, tracks );
+
+};
+
+THREE.AnimationClip.findByName = function( clipArray, name ) {
+
+ for ( var i = 0; i < clipArray.length; i ++ ) {
+
+ if ( clipArray[i].name === name ) {
+
+ return clipArray[i];
+
+ }
+ }
+
+ return null;
+
+};
+
+THREE.AnimationClip.CreateClipsFromMorphTargetSequences = function( morphTargets, fps ) {
+
+ var animationToMorphTargets = {};
+
+ // tested with https://regex101.com/ on trick sequences such flamingo_flyA_003, flamingo_run1_003, crdeath0059
+ var pattern = /^([\w-]*?)([\d]+)$/;
+
+ // sort morph target names into animation groups based patterns like Walk_001, Walk_002, Run_001, Run_002
+ for ( var i = 0, il = morphTargets.length; i < il; i ++ ) {
+
+ var morphTarget = morphTargets[ i ];
+ var parts = morphTarget.name.match( pattern );
+
+ if ( parts && parts.length > 1 ) {
+
+ var name = parts[ 1 ];
+
+ var animationMorphTargets = animationToMorphTargets[ name ];
+ if ( ! animationMorphTargets ) {
+ animationToMorphTargets[ name ] = animationMorphTargets = [];
+ }
+
+ animationMorphTargets.push( morphTarget );
+
+ }
+
+ }
+
+ var clips = [];
+
+ for ( var name in animationToMorphTargets ) {
+
+ clips.push( THREE.AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps ) );
+ }
+
+ return clips;
+
+};
+
+// parse the standard JSON format for clips
+THREE.AnimationClip.parse = function( json ) {
+
+ var tracks = [];
+
+ for ( var i = 0; i < json.tracks.length; i ++ ) {
+
+ tracks.push( THREE.KeyframeTrack.parse( json.tracks[i] ).scale( 1.0 / json.fps ) );
+
+ }
+
+ return new THREE.AnimationClip( json.name, json.duration, tracks );
+
+};
+
+
+// parse the animation.hierarchy format
+THREE.AnimationClip.parseAnimation = function( animation, bones, nodeName ) {
+
+ if ( ! animation ) {
+ console.error( " no animation in JSONLoader data" );
+ return null;
+ }
+
+ var convertTrack = function( trackName, animationKeys, propertyName, trackType, animationKeyToValueFunc ) {
+
+ var keys = [];
+
+ for ( var k = 0; k < animationKeys.length; k ++ ) {
+
+ var animationKey = animationKeys[k];
+
+ if ( animationKey[propertyName] !== undefined ) {
+
+ keys.push( { time: animationKey.time, value: animationKeyToValueFunc( animationKey ) } );
+ }
+
+ }
+
+ // only return track if there are actually keys.
+ if ( keys.length > 0 ) {
+
+ return new trackType( trackName, keys );
+
+ }
+
+ return null;
+
+ };
+
+ var tracks = [];
+
+ var clipName = animation.name || 'default';
+ var duration = animation.length || -1; // automatic length determination in AnimationClip.
+ var fps = animation.fps || 30;
+
+ var hierarchyTracks = animation.hierarchy || [];
+
+ for ( var h = 0; h < hierarchyTracks.length; h ++ ) {
+
+ var animationKeys = hierarchyTracks[ h ].keys;
+
+ // skip empty tracks
+ if ( ! animationKeys || animationKeys.length == 0 ) {
+ continue;
+ }
+
+ // process morph targets in a way exactly compatible with AnimationHandler.init( animation )
+ if ( animationKeys[0].morphTargets ) {
+
+ // figure out all morph targets used in this track
+ var morphTargetNames = {};
+ for ( var k = 0; k < animationKeys.length; k ++ ) {
+
+ if ( animationKeys[k].morphTargets ) {
+ for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) {
+
+ morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1;
+ }
+ }
+
+ }
+
+ // create a track for each morph target with all zero morphTargetInfluences except for the keys in which the morphTarget is named.
+ for ( var morphTargetName in morphTargetNames ) {
+
+ var keys = [];
+
+ for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) {
+
+ var animationKey = animationKeys[k];
+
+ keys.push( {
+ time: animationKey.time,
+ value: (( animationKey.morphTarget === morphTargetName ) ? 1 : 0 )
+ });
+
+ }
+
+ tracks.push( new THREE.NumberKeyframeTrack( nodeName + '.morphTargetInfluence[' + morphTargetName + ']', keys ) );
+
+ }
+
+ duration = morphTargetNames.length * ( fps || 1.0 );
+
+ } else {
+
+ var boneName = nodeName + '.bones[' + bones[ h ].name + ']';
+
+ // track contains positions...
+ var positionTrack = convertTrack( boneName + '.position', animationKeys, 'pos', THREE.VectorKeyframeTrack, function( animationKey ) {
+ return new THREE.Vector3().fromArray( animationKey.pos )
+ } );
+
+ if ( positionTrack ) tracks.push( positionTrack );
+
+ // track contains quaternions...
+ var quaternionTrack = convertTrack( boneName + '.quaternion', animationKeys, 'rot', THREE.QuaternionKeyframeTrack, function( animationKey ) {
+ if ( animationKey.rot.slerp ) {
+ return animationKey.rot.clone();
+ } else {
+ return new THREE.Quaternion().fromArray( animationKey.rot );
+ }
+ } );
+
+ if ( quaternionTrack ) tracks.push( quaternionTrack );
+
+ // track contains quaternions...
+ var scaleTrack = convertTrack( boneName + '.scale', animationKeys, 'scl', THREE.VectorKeyframeTrack, function( animationKey ) {
+ return new THREE.Vector3().fromArray( animationKey.scl )
+ } );
+
+ if ( scaleTrack ) tracks.push( scaleTrack );
+
+ }
+ }
+
+ if ( tracks.length === 0 ) {
+
+ return null;
+
+ }
+
+ var clip = new THREE.AnimationClip( clipName, duration, tracks );
+
+ return clip;
+
+};
+
+// File:src/animation/AnimationMixer.js
+
+/**
+ *
+ * Mixes together the AnimationClips scheduled by AnimationActions and applies them to the root and subtree
+ *
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.AnimationMixer = function( root ) {
+
+ this.root = root;
+ this.time = 0;
+ this.timeScale = 1.0;
+ this.actions = [];
+ this.propertyBindingMap = {};
+
+};
+
+THREE.AnimationMixer.prototype = {
+
+ constructor: THREE.AnimationMixer,
+
+ addAction: function( action ) {
+
+ // TODO: check for duplicate action names? Or provide each action with a UUID?
+
+ this.actions.push( action );
+ action.init( this.time );
+ action.mixer = this;
+
+ var tracks = action.clip.tracks;
+
+ var root = action.localRoot || this.root;
+
+ for ( var i = 0; i < tracks.length; i ++ ) {
+
+ var track = tracks[ i ];
+
+ var propertyBindingKey = root.uuid + '-' + track.name;
+ var propertyBinding = this.propertyBindingMap[ propertyBindingKey ];
+
+ if ( propertyBinding === undefined ) {
+
+ propertyBinding = new THREE.PropertyBinding( root, track.name );
+ this.propertyBindingMap[ propertyBindingKey ] = propertyBinding;
+
+ }
+
+ // push in the same order as the tracks.
+ action.propertyBindings.push( propertyBinding );
+
+ // track usages of shared property bindings, because if we leave too many around, the mixer can get slow
+ propertyBinding.referenceCount += 1;
+
+ }
+
+ },
+
+ removeAllActions: function() {
+
+ for ( var i = 0; i < this.actions.length; i ++ ) {
+
+ this.actions[i].mixer = null;
+
+ }
+
+ // unbind all property bindings
+ for ( var properyBindingKey in this.propertyBindingMap ) {
+
+ this.propertyBindingMap[ properyBindingKey ].unbind();
+
+ }
+
+ this.actions = [];
+ this.propertyBindingMap = {};
+
+ return this;
+
+ },
+
+ removeAction: function( action ) {
+
+ var index = this.actions.indexOf( action );
+
+ if ( index !== - 1 ) {
+
+ this.actions.splice( index, 1 );
+ action.mixer = null;
+
+ }
+
+
+ // remove unused property bindings because if we leave them around the mixer can get slow
+ var root = action.localRoot || this.root;
+ var tracks = action.clip.tracks;
+
+ for ( var i = 0; i < tracks.length; i ++ ) {
+
+ var track = tracks[ i ];
+
+ var propertyBindingKey = root.uuid + '-' + track.name;
+ var propertyBinding = this.propertyBindingMap[ propertyBindingKey ];
+
+ propertyBinding.referenceCount -= 1;
+
+ if ( propertyBinding.referenceCount <= 0 ) {
+
+ propertyBinding.unbind();
+
+ delete this.propertyBindingMap[ propertyBindingKey ];
+
+ }
+ }
+
+ return this;
+
+ },
+
+ // can be optimized if needed
+ findActionByName: function( name ) {
+
+ for ( var i = 0; i < this.actions.length; i ++ ) {
+
+ if ( this.actions[i].name === name ) return this.actions[i];
+
+ }
+
+ return null;
+
+ },
+
+ play: function( action, optionalFadeInDuration ) {
+
+ action.startTime = this.time;
+ this.addAction( action );
+
+ return this;
+
+ },
+
+ fadeOut: function( action, duration ) {
+
+ var keys = [];
+
+ keys.push( { time: this.time, value: 1 } );
+ keys.push( { time: this.time + duration, value: 0 } );
+
+ action.weight = new THREE.NumberKeyframeTrack( "weight", keys );
+
+ return this;
+
+ },
+
+ fadeIn: function( action, duration ) {
+
+ var keys = [];
+
+ keys.push( { time: this.time, value: 0 } );
+ keys.push( { time: this.time + duration, value: 1 } );
+
+ action.weight = new THREE.NumberKeyframeTrack( "weight", keys );
+
+ return this;
+
+ },
+
+ warp: function( action, startTimeScale, endTimeScale, duration ) {
+
+ var keys = [];
+
+ keys.push( { time: this.time, value: startTimeScale } );
+ keys.push( { time: this.time + duration, value: endTimeScale } );
+
+ action.timeScale = new THREE.NumberKeyframeTrack( "timeScale", keys );
+
+ return this;
+
+ },
+
+ crossFade: function( fadeOutAction, fadeInAction, duration, warp ) {
+
+ this.fadeOut( fadeOutAction, duration );
+ this.fadeIn( fadeInAction, duration );
+
+ if ( warp ) {
+
+ var startEndRatio = fadeOutAction.clip.duration / fadeInAction.clip.duration;
+ var endStartRatio = 1.0 / startEndRatio;
+
+ this.warp( fadeOutAction, 1.0, startEndRatio, duration );
+ this.warp( fadeInAction, endStartRatio, 1.0, duration );
+
+ }
+
+ return this;
+
+ },
+
+ update: function( deltaTime ) {
+
+ var mixerDeltaTime = deltaTime * this.timeScale;
+ this.time += mixerDeltaTime;
+
+ for ( var i = 0; i < this.actions.length; i ++ ) {
+
+ var action = this.actions[i];
+
+ var weight = action.getWeightAt( this.time );
+
+ var actionTimeScale = action.getTimeScaleAt( this.time );
+ var actionDeltaTime = mixerDeltaTime * actionTimeScale;
+
+ var actionResults = action.update( actionDeltaTime );
+
+ if ( action.weight <= 0 || ! action.enabled ) continue;
+
+ for ( var j = 0; j < actionResults.length; j ++ ) {
+
+ var name = action.clip.tracks[j].name;
+
+ action.propertyBindings[ j ].accumulate( actionResults[j], weight );
+
+ }
+
+ }
+
+ // apply to nodes
+ for ( var propertyBindingKey in this.propertyBindingMap ) {
+
+ this.propertyBindingMap[ propertyBindingKey ].apply();
+
+ }
+
+ return this;
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.AnimationMixer.prototype );
+
+// File:src/animation/AnimationUtils.js
+
+/**
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.AnimationUtils = {
+
+ getEqualsFunc: function( exemplarValue ) {
+
+ if ( exemplarValue.equals ) {
+ return function equals_object( a, b ) {
+ return a.equals( b );
+ }
+ }
+
+ return function equals_primitive( a, b ) {
+ return ( a === b );
+ };
+
+ },
+
+ clone: function( exemplarValue ) {
+
+ var typeName = typeof exemplarValue;
+ if ( typeName === "object" ) {
+ if ( exemplarValue.clone ) {
+ return exemplarValue.clone();
+ }
+ console.error( "can not figure out how to copy exemplarValue", exemplarValue );
+ }
+
+ return exemplarValue;
+
+ },
+
+ lerp: function( a, b, alpha, interTrack ) {
+
+ var lerpFunc = THREE.AnimationUtils.getLerpFunc( a, interTrack );
+
+ return lerpFunc( a, b, alpha );
+
+ },
+
+ lerp_object: function( a, b, alpha ) {
+ return a.lerp( b, alpha );
+ },
+
+ slerp_object: function( a, b, alpha ) {
+ return a.slerp( b, alpha );
+ },
+
+ lerp_number: function( a, b, alpha ) {
+ return a * ( 1 - alpha ) + b * alpha;
+ },
+
+ lerp_boolean: function( a, b, alpha ) {
+ return ( alpha < 0.5 ) ? a : b;
+ },
+
+ lerp_boolean_immediate: function( a, b, alpha ) {
+ return a;
+ },
+
+ lerp_string: function( a, b, alpha ) {
+ return ( alpha < 0.5 ) ? a : b;
+ },
+
+ lerp_string_immediate: function( a, b, alpha ) {
+ return a;
+ },
+
+ // NOTE: this is an accumulator function that modifies the first argument (e.g. a). This is to minimize memory alocations.
+ getLerpFunc: function( exemplarValue, interTrack ) {
+
+ if ( exemplarValue === undefined || exemplarValue === null ) throw new Error( "examplarValue is null" );
+
+ var typeName = typeof exemplarValue;
+
+ switch( typeName ) {
+
+ case "object":
+ if ( exemplarValue.lerp ) {
+ return THREE.AnimationUtils.lerp_object;
+ }
+
+ if ( exemplarValue.slerp ) {
+ return THREE.AnimationUtils.slerp_object;
+ }
+ break;
+
+ case "number":
+ return THREE.AnimationUtils.lerp_number;
+
+ case "boolean":
+ if ( interTrack ) {
+ return THREE.AnimationUtils.lerp_boolean;
+ } else {
+ return THREE.AnimationUtils.lerp_boolean_immediate;
+ }
+
+ case "string":
+ if ( interTrack ) {
+ return THREE.AnimationUtils.lerp_string;
+ } else {
+ return THREE.AnimationUtils.lerp_string_immediate;
+ }
+
+ }
+
+ }
+
+};
+
+// File:src/animation/KeyframeTrack.js
+
+/**
+ *
+ * A Track that returns a keyframe interpolated value, currently linearly interpolated
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.KeyframeTrack = function ( name, keys ) {
+
+ if ( name === undefined ) throw new Error( "track name is undefined" );
+ if ( keys === undefined || keys.length === 0 ) throw new Error( "no keys in track named " + name );
+
+ this.name = name;
+ this.keys = keys; // time in seconds, value as value
+
+ // the index of the last result, used as a starting point for local search.
+ this.lastIndex = 0;
+
+ this.validate();
+ this.optimize();
+
+};
+
+THREE.KeyframeTrack.prototype = {
+
+ constructor: THREE.KeyframeTrack,
+
+ getAt: function( time ) {
+
+
+ // this can not go higher than this.keys.length.
+ while( ( this.lastIndex < this.keys.length ) && ( time >= this.keys[this.lastIndex].time ) ) {
+ this.lastIndex ++;
+ };
+
+ // this can not go lower than 0.
+ while( ( this.lastIndex > 0 ) && ( time < this.keys[this.lastIndex - 1].time ) ) {
+ this.lastIndex --;
+ }
+
+ if ( this.lastIndex >= this.keys.length ) {
+
+ this.setResult( this.keys[ this.keys.length - 1 ].value );
+
+ return this.result;
+
+ }
+
+ if ( this.lastIndex === 0 ) {
+
+ this.setResult( this.keys[ 0 ].value );
+
+ return this.result;
+
+ }
+
+ var prevKey = this.keys[ this.lastIndex - 1 ];
+ this.setResult( prevKey.value );
+
+ // if true, means that prev/current keys are identical, thus no interpolation required.
+ if ( prevKey.constantToNext ) {
+
+ return this.result;
+
+ }
+
+ // linear interpolation to start with
+ var currentKey = this.keys[ this.lastIndex ];
+ var alpha = ( time - prevKey.time ) / ( currentKey.time - prevKey.time );
+ this.result = this.lerpValues( this.result, currentKey.value, alpha );
+
+ return this.result;
+
+ },
+
+ // move all keyframes either forwards or backwards in time
+ shift: function( timeOffset ) {
+
+ if ( timeOffset !== 0.0 ) {
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+ this.keys[i].time += timeOffset;
+ }
+
+ }
+
+ return this;
+
+ },
+
+ // scale all keyframe times by a factor (useful for frame <-> seconds conversions)
+ scale: function( timeScale ) {
+
+ if ( timeScale !== 1.0 ) {
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+ this.keys[i].time *= timeScale;
+ }
+
+ }
+
+ return this;
+
+ },
+
+ // removes keyframes before and after animation without changing any values within the range [startTime, endTime].
+ // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
+ trim: function( startTime, endTime ) {
+
+ var firstKeysToRemove = 0;
+ for ( var i = 1; i < this.keys.length; i ++ ) {
+ if ( this.keys[i] <= startTime ) {
+ firstKeysToRemove ++;
+ }
+ }
+
+ var lastKeysToRemove = 0;
+ for ( var i = this.keys.length - 2; i > 0; i ++ ) {
+ if ( this.keys[i] >= endTime ) {
+ lastKeysToRemove ++;
+ } else {
+ break;
+ }
+ }
+
+ // remove last keys first because it doesn't affect the position of the first keys (the otherway around doesn't work as easily)
+ if ( ( firstKeysToRemove + lastKeysToRemove ) > 0 ) {
+ this.keys = this.keys.splice( firstKeysToRemove, this.keys.length - lastKeysToRemove - firstKeysToRemove );;
+ }
+
+ return this;
+
+ },
+
+ /* NOTE: This is commented out because we really shouldn't have to handle unsorted key lists
+ Tracks with out of order keys should be considered to be invalid. - bhouston
+ sort: function() {
+
+ this.keys.sort( THREE.KeyframeTrack.keyComparer );
+
+ return this;
+
+ },*/
+
+ // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
+ // One could eventually ensure that all key.values in a track are all of the same type (otherwise interpolation makes no sense.)
+ validate: function() {
+
+ var prevKey = null;
+
+ if ( this.keys.length === 0 ) {
+ console.error( " track is empty, no keys", this );
+ return;
+ }
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+
+ var currKey = this.keys[i];
+
+ if ( ! currKey ) {
+ console.error( " key is null in track", this, i );
+ return;
+ }
+
+ if ( ( typeof currKey.time ) !== 'number' || isNaN( currKey.time ) ) {
+ console.error( " key.time is not a valid number", this, i, currKey );
+ return;
+ }
+
+ if ( currKey.value === undefined || currKey.value === null) {
+ console.error( " key.value is null in track", this, i, currKey );
+ return;
+ }
+
+ if ( prevKey && prevKey.time > currKey.time ) {
+ console.error( " key.time is less than previous key time, out of order keys", this, i, currKey, prevKey );
+ return;
+ }
+
+ prevKey = currKey;
+
+ }
+
+ return this;
+
+ },
+
+ // currently only removes equivalent sequential keys (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0), which are common in morph target animations
+ optimize: function() {
+
+ var newKeys = [];
+ var prevKey = this.keys[0];
+ newKeys.push( prevKey );
+
+ var equalsFunc = THREE.AnimationUtils.getEqualsFunc( prevKey.value );
+
+ for ( var i = 1; i < this.keys.length - 1; i ++ ) {
+ var currKey = this.keys[i];
+ var nextKey = this.keys[i+1];
+
+ // if prevKey & currKey are the same time, remove currKey. If you want immediate adjacent keys, use an epsilon offset
+ // it is not possible to have two keys at the same time as we sort them. The sort is not stable on keys with the same time.
+ if ( ( prevKey.time === currKey.time ) ) {
+
+ continue;
+
+ }
+
+ // remove completely unnecessary keyframes that are the same as their prev and next keys
+ if ( this.compareValues( prevKey.value, currKey.value ) && this.compareValues( currKey.value, nextKey.value ) ) {
+
+ continue;
+
+ }
+
+ // determine if interpolation is required
+ prevKey.constantToNext = this.compareValues( prevKey.value, currKey.value );
+
+ newKeys.push( currKey );
+ prevKey = currKey;
+ }
+ newKeys.push( this.keys[ this.keys.length - 1 ] );
+
+ this.keys = newKeys;
+
+ return this;
+
+ }
+
+};
+
+THREE.KeyframeTrack.keyComparer = function keyComparator(key0, key1) {
+ return key0.time - key1.time;
+};
+
+THREE.KeyframeTrack.parse = function( json ) {
+
+ if ( json.type === undefined ) throw new Error( "track type undefined, can not parse" );
+
+ var trackType = THREE.KeyframeTrack.GetTrackTypeForTypeName( json.type );
+
+ return trackType.parse( json );
+
+};
+
+THREE.KeyframeTrack.GetTrackTypeForTypeName = function( typeName ) {
+ switch( typeName.toLowerCase() ) {
+ case "vector":
+ case "vector2":
+ case "vector3":
+ case "vector4":
+ return THREE.VectorKeyframeTrack;
+
+ case "quaternion":
+ return THREE.QuaternionKeyframeTrack;
+
+ case "integer":
+ case "scalar":
+ case "double":
+ case "float":
+ case "number":
+ return THREE.NumberKeyframeTrack;
+
+ case "bool":
+ case "boolean":
+ return THREE.BooleanKeyframeTrack;
+
+ case "string":
+ return THREE.StringKeyframeTrack;
+ };
+
+ throw new Error( "Unsupported typeName: " + typeName );
+};
+
+// File:src/animation/PropertyBinding.js
+
+/**
+ *
+ * A track bound to a real value in the scene graph.
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.PropertyBinding = function ( rootNode, trackName ) {
+
+ this.rootNode = rootNode;
+ this.trackName = trackName;
+ this.referenceCount = 0;
+ this.originalValue = null; // the value of the property before it was controlled by this binding
+
+ var parseResults = THREE.PropertyBinding.parseTrackName( trackName );
+
+ this.directoryName = parseResults.directoryName;
+ this.nodeName = parseResults.nodeName;
+ this.objectName = parseResults.objectName;
+ this.objectIndex = parseResults.objectIndex;
+ this.propertyName = parseResults.propertyName;
+ this.propertyIndex = parseResults.propertyIndex;
+
+ this.node = THREE.PropertyBinding.findNode( rootNode, this.nodeName ) || rootNode;
+
+ this.cumulativeValue = null;
+ this.cumulativeWeight = 0;
+};
+
+THREE.PropertyBinding.prototype = {
+
+ constructor: THREE.PropertyBinding,
+
+ reset: function() {
+
+ this.cumulativeValue = null;
+ this.cumulativeWeight = 0;
+
+ },
+
+ accumulate: function( value, weight ) {
+
+ if ( ! this.isBound ) this.bind();
+
+ if ( this.cumulativeWeight === 0 ) {
+
+ if ( weight > 0 ) {
+
+ if ( this.cumulativeValue === null ) {
+ this.cumulativeValue = THREE.AnimationUtils.clone( value );
+ }
+ this.cumulativeWeight = weight;
+
+ }
+
+ } else {
+
+ var lerpAlpha = weight / ( this.cumulativeWeight + weight );
+ this.cumulativeValue = this.lerpValue( this.cumulativeValue, value, lerpAlpha );
+ this.cumulativeWeight += weight;
+
+ }
+
+ },
+
+ unbind: function() {
+
+ if ( ! this.isBound ) return;
+
+ this.setValue( this.originalValue );
+
+ this.setValue = null;
+ this.getValue = null;
+ this.lerpValue = null;
+ this.equalsValue = null;
+ this.triggerDirty = null;
+ this.isBound = false;
+
+ },
+
+ // bind to the real property in the scene graph, remember original value, memorize various accessors for speed/inefficiency
+ bind: function() {
+
+ if ( this.isBound ) return;
+
+ var targetObject = this.node;
+
+ // ensure there is a value node
+ if ( ! targetObject ) {
+ console.error( " trying to update node for track: " + this.trackName + " but it wasn't found." );
+ return;
+ }
+
+ if ( this.objectName ) {
+ // special case were we need to reach deeper into the hierarchy to get the face materials....
+ if ( this.objectName === "materials" ) {
+ if ( ! targetObject.material ) {
+ console.error( ' can not bind to material as node does not have a material', this );
+ return;
+ }
+ if ( ! targetObject.material.materials ) {
+ console.error( ' can not bind to material.materials as node.material does not have a materials array', this );
+ return;
+ }
+ targetObject = targetObject.material.materials;
+ } else if ( this.objectName === "bones" ) {
+ if ( ! targetObject.skeleton ) {
+ console.error( ' can not bind to bones as node does not have a skeleton', this );
+ return;
+ }
+ // potential future optimization: skip this if propertyIndex is already an integer, and convert the integer string to a true integer.
+
+ targetObject = targetObject.skeleton.bones;
+
+ // support resolving morphTarget names into indices.
+ for ( var i = 0; i < targetObject.length; i ++ ) {
+ if ( targetObject[i].name === this.objectIndex ) {
+ this.objectIndex = i;
+ break;
+ }
+ }
+ } else {
+
+ if ( targetObject[ this.objectName ] === undefined ) {
+ console.error( ' can not bind to objectName of node, undefined', this );
+ return;
+ }
+ targetObject = targetObject[ this.objectName ];
+ }
+
+ if ( this.objectIndex !== undefined ) {
+ if ( targetObject[ this.objectIndex ] === undefined ) {
+ console.error( " trying to bind to objectIndex of objectName, but is undefined:", this, targetObject );
+ return;
+ }
+
+ targetObject = targetObject[ this.objectIndex ];
+ }
+
+ }
+
+ // special case mappings
+ var nodeProperty = targetObject[ this.propertyName ];
+ if ( ! nodeProperty ) {
+ console.error( " trying to update property for track: " + this.nodeName + '.' + this.propertyName + " but it wasn't found.", targetObject );
+ return;
+ }
+
+ // access a sub element of the property array (only primitives are supported right now)
+ if ( this.propertyIndex !== undefined ) {
+
+ if ( this.propertyName === "morphTargetInfluences" ) {
+ // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer.
+
+ // support resolving morphTarget names into indices.
+ if ( ! targetObject.geometry ) {
+ console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry', this );
+ }
+ if ( ! targetObject.geometry.morphTargets ) {
+ console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this );
+ }
+
+ for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) {
+ if ( targetObject.geometry.morphTargets[i].name === this.propertyIndex ) {
+ this.propertyIndex = i;
+ break;
+ }
+ }
+ }
+
+ this.setValue = function setValue_propertyIndexed( value ) {
+ if ( ! this.equalsValue( nodeProperty[ this.propertyIndex ], value ) ) {
+ nodeProperty[ this.propertyIndex ] = value;
+ return true;
+ }
+ return false;
+ };
+
+ this.getValue = function getValue_propertyIndexed() {
+ return nodeProperty[ this.propertyIndex ];
+ };
+
+ }
+ // must use copy for Object3D.Euler/Quaternion
+ else if ( nodeProperty.copy ) {
+
+ this.setValue = function setValue_propertyObject( value ) {
+ if ( ! this.equalsValue( nodeProperty, value ) ) {
+ nodeProperty.copy( value );
+ return true;
+ }
+ return false;
+ }
+
+ this.getValue = function getValue_propertyObject() {
+ return nodeProperty;
+ };
+
+ }
+ // otherwise just set the property directly on the node (do not use nodeProperty as it may not be a reference object)
+ else {
+
+ this.setValue = function setValue_property( value ) {
+ if ( ! this.equalsValue( targetObject[ this.propertyName ], value ) ) {
+ targetObject[ this.propertyName ] = value;
+ return true;
+ }
+ return false;
+ }
+
+ this.getValue = function getValue_property() {
+ return targetObject[ this.propertyName ];
+ };
+
+ }
+
+ // trigger node dirty
+ if ( targetObject.needsUpdate !== undefined ) { // material
+
+ this.triggerDirty = function triggerDirty_needsUpdate() {
+ this.node.needsUpdate = true;
+ }
+
+ } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform
+
+ this.triggerDirty = function triggerDirty_matrixWorldNeedsUpdate() {
+ targetObject.matrixWorldNeedsUpdate = true;
+ }
+
+ }
+
+ this.originalValue = this.getValue();
+
+ this.equalsValue = THREE.AnimationUtils.getEqualsFunc( this.originalValue );
+ this.lerpValue = THREE.AnimationUtils.getLerpFunc( this.originalValue, true );
+
+ this.isBound = true;
+
+ },
+
+ apply: function() {
+
+ // for speed capture the setter pattern as a closure (sort of a memoization pattern: https://en.wikipedia.org/wiki/Memoization)
+ if ( ! this.isBound ) this.bind();
+
+ // early exit if there is nothing to apply.
+ if ( this.cumulativeWeight > 0 ) {
+
+ // blend with original value
+ if ( this.cumulativeWeight < 1 ) {
+
+ var remainingWeight = 1 - this.cumulativeWeight;
+ var lerpAlpha = remainingWeight / ( this.cumulativeWeight + remainingWeight );
+ this.cumulativeValue = this.lerpValue( this.cumulativeValue, this.originalValue, lerpAlpha );
+
+ }
+
+ var valueChanged = this.setValue( this.cumulativeValue );
+
+ if ( valueChanged && this.triggerDirty ) {
+ this.triggerDirty();
+ }
+
+ // reset accumulator
+ this.cumulativeValue = null;
+ this.cumulativeWeight = 0;
+
+ }
+ }
+
+};
+
+
+THREE.PropertyBinding.parseTrackName = function( trackName ) {
+
+ // matches strings in the form of:
+ // nodeName.property
+ // nodeName.property[accessor]
+ // nodeName.material.property[accessor]
+ // uuid.property[accessor]
+ // uuid.objectName[objectIndex].propertyName[propertyIndex]
+ // parentName/nodeName.property
+ // parentName/parentName/nodeName.property[index]
+ // .bone[Armature.DEF_cog].position
+ // created and tested via https://regex101.com/#javascript
+
+ var re = /^(([\w]+\/)*)([\w-\d]+)?(\.([\w]+)(\[([\w\d\[\]\_. ]+)\])?)?(\.([\w.]+)(\[([\w\d\[\]\_. ]+)\])?)$/;
+ var matches = re.exec(trackName);
+
+ if ( ! matches ) {
+ throw new Error( "cannot parse trackName at all: " + trackName );
+ }
+
+ if (matches.index === re.lastIndex) {
+ re.lastIndex++;
+ }
+
+ var results = {
+ directoryName: matches[1],
+ nodeName: matches[3], // allowed to be null, specified root node.
+ objectName: matches[5],
+ objectIndex: matches[7],
+ propertyName: matches[9],
+ propertyIndex: matches[11] // allowed to be null, specifies that the whole property is set.
+ };
+
+ if ( results.propertyName === null || results.propertyName.length === 0 ) {
+ throw new Error( "can not parse propertyName from trackName: " + trackName );
+ }
+
+ return results;
+
+};
+
+THREE.PropertyBinding.findNode = function( root, nodeName ) {
+
+ function searchSkeleton( skeleton ) {
+
+ for ( var i = 0; i < skeleton.bones.length; i ++ ) {
+
+ var bone = skeleton.bones[i];
+
+ if ( bone.name === nodeName ) {
+
+ return bone;
+
+ }
+ }
+
+ return null;
+
+ }
+
+ function searchNodeSubtree( children ) {
+
+ for ( var i = 0; i < children.length; i ++ ) {
+
+ var childNode = children[i];
+
+ if ( childNode.name === nodeName || childNode.uuid === nodeName ) {
+
+ return childNode;
+
+ }
+
+ var result = searchNodeSubtree( childNode.children );
+
+ if ( result ) return result;
+
+ }
+
+ return null;
+
+ }
+
+ //
+
+ if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) {
+
+ return root;
+
+ }
+
+ // search into skeleton bones.
+ if ( root.skeleton ) {
+
+ var bone = searchSkeleton( root.skeleton );
+
+ if ( bone ) {
+
+ return bone;
+
+ }
+ }
+
+ // search into node subtree.
+ if ( root.children ) {
+
+ var subTreeNode = searchNodeSubtree( root.children );
+
+ if ( subTreeNode ) {
+
+ return subTreeNode;
+
+ }
+
+ }
+
+ return null;
+}
+
+// File:src/animation/tracks/VectorKeyframeTrack.js
+
+/**
+ *
+ * A Track that interpolates Vectors
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.VectorKeyframeTrack = function ( name, keys ) {
+
+ THREE.KeyframeTrack.call( this, name, keys );
+
+ // local cache of value type to avoid allocations during runtime.
+ this.result = this.keys[0].value.clone();
+
+};
+
+THREE.VectorKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );
+
+THREE.VectorKeyframeTrack.prototype.constructor = THREE.VectorKeyframeTrack;
+
+THREE.VectorKeyframeTrack.prototype.setResult = function( value ) {
+
+ this.result.copy( value );
+
+};
+
+// memoization of the lerp function for speed.
+// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
+THREE.VectorKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {
+
+ return value0.lerp( value1, alpha );
+
+};
+
+THREE.VectorKeyframeTrack.prototype.compareValues = function( value0, value1 ) {
+
+ return value0.equals( value1 );
+
+};
+
+THREE.VectorKeyframeTrack.prototype.clone = function() {
+
+ var clonedKeys = [];
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+
+ var key = this.keys[i];
+ clonedKeys.push( {
+ time: key.time,
+ value: key.value.clone()
+ } );
+ }
+
+ return new THREE.VectorKeyframeTrack( this.name, clonedKeys );
+
+};
+
+THREE.VectorKeyframeTrack.parse = function( json ) {
+
+ var elementCount = json.keys[0].value.length;
+ var valueType = THREE[ 'Vector' + elementCount ];
+
+ var keys = [];
+
+ for ( var i = 0; i < json.keys.length; i ++ ) {
+ var jsonKey = json.keys[i];
+ keys.push( {
+ value: new valueType().fromArray( jsonKey.value ),
+ time: jsonKey.time
+ } );
+ }
+
+ return new THREE.VectorKeyframeTrack( json.name, keys );
+
+};
+
+// File:src/animation/tracks/QuaternionKeyframeTrack.js
+
+/**
+ *
+ * A Track that interpolates Quaternion
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.QuaternionKeyframeTrack = function ( name, keys ) {
+
+ THREE.KeyframeTrack.call( this, name, keys );
+
+ // local cache of value type to avoid allocations during runtime.
+ this.result = this.keys[0].value.clone();
+
+};
+
+THREE.QuaternionKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );
+
+THREE.QuaternionKeyframeTrack.prototype.constructor = THREE.QuaternionKeyframeTrack;
+
+THREE.QuaternionKeyframeTrack.prototype.setResult = function( value ) {
+
+ this.result.copy( value );
+
+};
+
+// memoization of the lerp function for speed.
+// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
+THREE.QuaternionKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {
+
+ return value0.slerp( value1, alpha );
+
+};
+
+THREE.QuaternionKeyframeTrack.prototype.compareValues = function( value0, value1 ) {
+
+ return value0.equals( value1 );
+
+};
+
+THREE.QuaternionKeyframeTrack.prototype.multiply = function( quat ) {
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+
+ this.keys[i].value.multiply( quat );
+
+ }
+
+ return this;
+
+};
+
+THREE.QuaternionKeyframeTrack.prototype.clone = function() {
+
+ var clonedKeys = [];
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+
+ var key = this.keys[i];
+ clonedKeys.push( {
+ time: key.time,
+ value: key.value.clone()
+ } );
+ }
+
+ return new THREE.QuaternionKeyframeTrack( this.name, clonedKeys );
+
+};
+
+THREE.QuaternionKeyframeTrack.parse = function( json ) {
+
+ var keys = [];
+
+ for ( var i = 0; i < json.keys.length; i ++ ) {
+ var jsonKey = json.keys[i];
+ keys.push( {
+ value: new THREE.Quaternion().fromArray( jsonKey.value ),
+ time: jsonKey.time
+ } );
+ }
+
+ return new THREE.QuaternionKeyframeTrack( json.name, keys );
+
+};
+
+// File:src/animation/tracks/StringKeyframeTrack.js
+
+/**
+ *
+ * A Track that interpolates Strings
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.StringKeyframeTrack = function ( name, keys ) {
+
+ THREE.KeyframeTrack.call( this, name, keys );
+
+ // local cache of value type to avoid allocations during runtime.
+ this.result = this.keys[0].value;
+
+};
+
+THREE.StringKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );
+
+THREE.StringKeyframeTrack.prototype.constructor = THREE.StringKeyframeTrack;
+
+THREE.StringKeyframeTrack.prototype.setResult = function( value ) {
+
+ this.result = value;
+
+};
+
+// memoization of the lerp function for speed.
+// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
+THREE.StringKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {
+
+ return ( alpha < 1.0 ) ? value0 : value1;
+
+};
+
+THREE.StringKeyframeTrack.prototype.compareValues = function( value0, value1 ) {
+
+ return ( value0 === value1 );
+
+};
+
+THREE.StringKeyframeTrack.prototype.clone = function() {
+
+ var clonedKeys = [];
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+
+ var key = this.keys[i];
+ clonedKeys.push( {
+ time: key.time,
+ value: key.value
+ } );
+ }
+
+ return new THREE.StringKeyframeTrack( this.name, clonedKeys );
+
+};
+
+THREE.StringKeyframeTrack.parse = function( json ) {
+
+ return new THREE.StringKeyframeTrack( json.name, json.keys );
+
+};
+
+// File:src/animation/tracks/BooleanKeyframeTrack.js
+
+/**
+ *
+ * A Track that interpolates Boolean
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.BooleanKeyframeTrack = function ( name, keys ) {
+
+ THREE.KeyframeTrack.call( this, name, keys );
+
+ // local cache of value type to avoid allocations during runtime.
+ this.result = this.keys[0].value;
+
+};
+
+THREE.BooleanKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );
+
+THREE.BooleanKeyframeTrack.prototype.constructor = THREE.BooleanKeyframeTrack;
+
+THREE.BooleanKeyframeTrack.prototype.setResult = function( value ) {
+
+ this.result = value;
+
+};
+
+// memoization of the lerp function for speed.
+// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
+THREE.BooleanKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {
+
+ return ( alpha < 1.0 ) ? value0 : value1;
+
+};
+
+THREE.BooleanKeyframeTrack.prototype.compareValues = function( value0, value1 ) {
+
+ return ( value0 === value1 );
+
+};
+
+THREE.BooleanKeyframeTrack.prototype.clone = function() {
+
+ var clonedKeys = [];
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+
+ var key = this.keys[i];
+ clonedKeys.push( {
+ time: key.time,
+ value: key.value
+ } );
+ }
+
+ return new THREE.BooleanKeyframeTrack( this.name, clonedKeys );
+
+};
+
+THREE.BooleanKeyframeTrack.parse = function( json ) {
+
+ return new THREE.BooleanKeyframeTrack( json.name, json.keys );
+
+};
+
+// File:src/animation/tracks/NumberKeyframeTrack.js
+
+/**
+ *
+ * A Track that interpolates Numbers
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.NumberKeyframeTrack = function ( name, keys ) {
+
+ THREE.KeyframeTrack.call( this, name, keys );
+
+ // local cache of value type to avoid allocations during runtime.
+ this.result = this.keys[0].value;
+
+};
+
+THREE.NumberKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );
+
+THREE.NumberKeyframeTrack.prototype.constructor = THREE.NumberKeyframeTrack;
+
+THREE.NumberKeyframeTrack.prototype.setResult = function( value ) {
+
+ this.result = value;
+
+};
+
+// memoization of the lerp function for speed.
+// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
+THREE.NumberKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {
+
+ return value0 * ( 1 - alpha ) + value1 * alpha;
+
+};
+
+THREE.NumberKeyframeTrack.prototype.compareValues = function( value0, value1 ) {
+
+ return ( value0 === value1 );
+
+};
+
+THREE.NumberKeyframeTrack.prototype.clone = function() {
+
+ var clonedKeys = [];
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+
+ var key = this.keys[i];
+ clonedKeys.push( {
+ time: key.time,
+ value: key.value
+ } );
+ }
+
+ return new THREE.NumberKeyframeTrack( this.name, clonedKeys );
+
+};
+
+THREE.NumberKeyframeTrack.parse = function( json ) {
+
+ return new THREE.NumberKeyframeTrack( json.name, json.keys );
+
+};
+
+// File:src/cameras/Camera.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author WestLangley / http://github.com/WestLangley
+*/
+
+THREE.Camera = function () {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Camera';
+
+ this.matrixWorldInverse = new THREE.Matrix4();
+ this.projectionMatrix = new THREE.Matrix4();
+
+};
+
+THREE.Camera.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Camera.prototype.constructor = THREE.Camera;
+
+THREE.Camera.prototype.getWorldDirection = function () {
+
+ var quaternion = new THREE.Quaternion();
+
+ return function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ this.getWorldQuaternion( quaternion );
+
+ return result.set( 0, 0, - 1 ).applyQuaternion( quaternion );
+
+ };
+
+}();
+
+THREE.Camera.prototype.lookAt = function () {
+
+ // This routine does not support cameras with rotated and/or translated parent(s)
+
+ var m1 = new THREE.Matrix4();
+
+ return function ( vector ) {
+
+ m1.lookAt( this.position, vector, this.up );
+
+ this.quaternion.setFromRotationMatrix( m1 );
+
+ };
+
+}();
+
+THREE.Camera.prototype.clone = function () {
+
+ return new this.constructor().copy( this );
+
+};
+
+THREE.Camera.prototype.copy = function ( source ) {
+
+ THREE.Object3D.prototype.copy.call( this, source );
+
+ this.matrixWorldInverse.copy( source.matrixWorldInverse );
+ this.projectionMatrix.copy( source.projectionMatrix );
+
+ return this;
+
+};
+
+// File:src/cameras/CubeCamera.js
+
+/**
+ * Camera for rendering cube maps
+ * - renders scene into axis-aligned cube
+ *
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.CubeCamera = function ( near, far, cubeResolution ) {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'CubeCamera';
+
+ var fov = 90, aspect = 1;
+
+ var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far );
+ cameraPX.up.set( 0, - 1, 0 );
+ cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) );
+ this.add( cameraPX );
+
+ var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far );
+ cameraNX.up.set( 0, - 1, 0 );
+ cameraNX.lookAt( new THREE.Vector3( - 1, 0, 0 ) );
+ this.add( cameraNX );
+
+ var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far );
+ cameraPY.up.set( 0, 0, 1 );
+ cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) );
+ this.add( cameraPY );
+
+ var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far );
+ cameraNY.up.set( 0, 0, - 1 );
+ cameraNY.lookAt( new THREE.Vector3( 0, - 1, 0 ) );
+ this.add( cameraNY );
+
+ var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far );
+ cameraPZ.up.set( 0, - 1, 0 );
+ cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) );
+ this.add( cameraPZ );
+
+ var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far );
+ cameraNZ.up.set( 0, - 1, 0 );
+ cameraNZ.lookAt( new THREE.Vector3( 0, 0, - 1 ) );
+ this.add( cameraNZ );
+
+ this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } );
+
+ this.updateCubeMap = function ( renderer, scene ) {
+
+ if ( this.parent === null ) this.updateMatrixWorld();
+
+ var renderTarget = this.renderTarget;
+ var generateMipmaps = renderTarget.texture.generateMipmaps;
+
+ renderTarget.texture.generateMipmaps = false;
+
+ renderTarget.activeCubeFace = 0;
+ renderer.render( scene, cameraPX, renderTarget );
+
+ renderTarget.activeCubeFace = 1;
+ renderer.render( scene, cameraNX, renderTarget );
+
+ renderTarget.activeCubeFace = 2;
+ renderer.render( scene, cameraPY, renderTarget );
+
+ renderTarget.activeCubeFace = 3;
+ renderer.render( scene, cameraNY, renderTarget );
+
+ renderTarget.activeCubeFace = 4;
+ renderer.render( scene, cameraPZ, renderTarget );
+
+ renderTarget.texture.generateMipmaps = generateMipmaps;
+
+ renderTarget.activeCubeFace = 5;
+ renderer.render( scene, cameraNZ, renderTarget );
+
+ renderer.setRenderTarget( null );
+
+ };
+
+};
+
+THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype );
+THREE.CubeCamera.prototype.constructor = THREE.CubeCamera;
+
+// File:src/cameras/OrthographicCamera.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) {
+
+ THREE.Camera.call( this );
+
+ this.type = 'OrthographicCamera';
+
+ this.zoom = 1;
+
+ this.left = left;
+ this.right = right;
+ this.top = top;
+ this.bottom = bottom;
+
+ this.near = ( near !== undefined ) ? near : 0.1;
+ this.far = ( far !== undefined ) ? far : 2000;
+
+ this.updateProjectionMatrix();
+
+};
+
+THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype );
+THREE.OrthographicCamera.prototype.constructor = THREE.OrthographicCamera;
+
+THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () {
+
+ var dx = ( this.right - this.left ) / ( 2 * this.zoom );
+ var dy = ( this.top - this.bottom ) / ( 2 * this.zoom );
+ var cx = ( this.right + this.left ) / 2;
+ var cy = ( this.top + this.bottom ) / 2;
+
+ this.projectionMatrix.makeOrthographic( cx - dx, cx + dx, cy + dy, cy - dy, this.near, this.far );
+
+};
+
+THREE.OrthographicCamera.prototype.copy = function ( source ) {
+
+ THREE.Camera.prototype.copy.call( this, source );
+
+ this.left = source.left;
+ this.right = source.right;
+ this.top = source.top;
+ this.bottom = source.bottom;
+ this.near = source.near;
+ this.far = source.far;
+
+ this.zoom = source.zoom;
+
+ return this;
+
+};
+
+THREE.OrthographicCamera.prototype.toJSON = function ( meta ) {
+
+ var data = THREE.Object3D.prototype.toJSON.call( this, meta );
+
+ data.object.zoom = this.zoom;
+ data.object.left = this.left;
+ data.object.right = this.right;
+ data.object.top = this.top;
+ data.object.bottom = this.bottom;
+ data.object.near = this.near;
+ data.object.far = this.far;
+
+ return data;
+
+};
+
+// File:src/cameras/PerspectiveCamera.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author greggman / http://games.greggman.com/
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ */
+
+THREE.PerspectiveCamera = function ( fov, aspect, near, far ) {
+
+ THREE.Camera.call( this );
+
+ this.type = 'PerspectiveCamera';
+
+ this.zoom = 1;
+
+ this.fov = fov !== undefined ? fov : 50;
+ this.aspect = aspect !== undefined ? aspect : 1;
+ this.near = near !== undefined ? near : 0.1;
+ this.far = far !== undefined ? far : 2000;
+
+ this.updateProjectionMatrix();
+
+};
+
+THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype );
+THREE.PerspectiveCamera.prototype.constructor = THREE.PerspectiveCamera;
+
+
+/**
+ * Uses Focal Length (in mm) to estimate and set FOV
+ * 35mm (full-frame) camera is used if frame size is not specified;
+ * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html
+ */
+
+THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) {
+
+ if ( frameHeight === undefined ) frameHeight = 24;
+
+ this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) );
+ this.updateProjectionMatrix();
+
+};
+
+
+/**
+ * Sets an offset in a larger frustum. This is useful for multi-window or
+ * multi-monitor/multi-machine setups.
+ *
+ * For example, if you have 3x2 monitors and each monitor is 1920x1080 and
+ * the monitors are in grid like this
+ *
+ * +---+---+---+
+ * | A | B | C |
+ * +---+---+---+
+ * | D | E | F |
+ * +---+---+---+
+ *
+ * then for each monitor you would call it like this
+ *
+ * var w = 1920;
+ * var h = 1080;
+ * var fullWidth = w * 3;
+ * var fullHeight = h * 2;
+ *
+ * --A--
+ * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );
+ * --B--
+ * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );
+ * --C--
+ * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );
+ * --D--
+ * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );
+ * --E--
+ * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );
+ * --F--
+ * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );
+ *
+ * Note there is no reason monitors have to be the same size or in a grid.
+ */
+
+THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) {
+
+ this.fullWidth = fullWidth;
+ this.fullHeight = fullHeight;
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+
+ this.updateProjectionMatrix();
+
+};
+
+
+THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () {
+
+ var fov = THREE.Math.radToDeg( 2 * Math.atan( Math.tan( THREE.Math.degToRad( this.fov ) * 0.5 ) / this.zoom ) );
+
+ if ( this.fullWidth ) {
+
+ var aspect = this.fullWidth / this.fullHeight;
+ var top = Math.tan( THREE.Math.degToRad( fov * 0.5 ) ) * this.near;
+ var bottom = - top;
+ var left = aspect * bottom;
+ var right = aspect * top;
+ var width = Math.abs( right - left );
+ var height = Math.abs( top - bottom );
+
+ this.projectionMatrix.makeFrustum(
+ left + this.x * width / this.fullWidth,
+ left + ( this.x + this.width ) * width / this.fullWidth,
+ top - ( this.y + this.height ) * height / this.fullHeight,
+ top - this.y * height / this.fullHeight,
+ this.near,
+ this.far
+ );
+
+ } else {
+
+ this.projectionMatrix.makePerspective( fov, this.aspect, this.near, this.far );
+
+ }
+
+};
+
+THREE.PerspectiveCamera.prototype.copy = function ( source ) {
+
+ THREE.Camera.prototype.copy.call( this, source );
+
+ this.fov = source.fov;
+ this.aspect = source.aspect;
+ this.near = source.near;
+ this.far = source.far;
+
+ this.zoom = source.zoom;
+
+ return this;
+
+};
+
+THREE.PerspectiveCamera.prototype.toJSON = function ( meta ) {
+
+ var data = THREE.Object3D.prototype.toJSON.call( this, meta );
+
+ data.object.zoom = this.zoom;
+ data.object.fov = this.fov;
+ data.object.aspect = this.aspect;
+ data.object.near = this.near;
+ data.object.far = this.far;
+
+ return data;
+
+};
+
+// File:src/lights/Light.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Light = function ( color ) {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Light';
+
+ this.color = new THREE.Color( color );
+
+ this.receiveShadow = undefined;
+
+};
+
+THREE.Light.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Light.prototype.constructor = THREE.Light;
+
+Object.defineProperties( THREE.Light.prototype, {
+ onlyShadow: {
+ set: function ( value ) {
+ console.warn( 'THREE.Light: .onlyShadow has been removed.' );
+ }
+ },
+ shadowCameraFov: {
+ set: function ( value ) {
+ this.shadow.camera.fov = value;
+ }
+ },
+ shadowCameraLeft: {
+ set: function ( value ) {
+ this.shadow.camera.left = value;
+ }
+ },
+ shadowCameraRight: {
+ set: function ( value ) {
+ this.shadow.camera.right = value;
+ }
+ },
+ shadowCameraTop: {
+ set: function ( value ) {
+ this.shadow.camera.top = value;
+ }
+ },
+ shadowCameraBottom: {
+ set: function ( value ) {
+ this.shadow.camera.bottom = value;
+ }
+ },
+ shadowCameraNear: {
+ set: function ( value ) {
+ this.shadow.camera.near = value;
+ }
+ },
+ shadowCameraFar: {
+ set: function ( value ) {
+ this.shadow.camera.far = value;
+ }
+ },
+ shadowCameraVisible: {
+ set: function ( value ) {
+ console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow ) instead.' );
+ }
+ },
+ shadowBias: {
+ set: function ( value ) {
+ this.shadow.bias = value;
+ }
+ },
+ shadowDarkness: {
+ set: function ( value ) {
+ this.shadow.darkness = value;
+ }
+ },
+ shadowMapWidth: {
+ set: function ( value ) {
+ this.shadow.mapSize.width = value;
+ }
+ },
+ shadowMapHeight: {
+ set: function ( value ) {
+ this.shadow.mapSize.height = value;
+ }
+ }
+} );
+
+THREE.Light.prototype.copy = function ( source ) {
+
+ THREE.Object3D.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+
+ return this;
+
+};
+
+THREE.Light.prototype.toJSON = function ( meta ) {
+
+ var data = THREE.Object3D.prototype.toJSON.call( this, meta );
+
+ data.object.color = this.color.getHex();
+ if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex();
+
+ if ( this.intensity !== undefined ) data.object.intensity = this.intensity;
+ if ( this.distance !== undefined ) data.object.distance = this.distance;
+ if ( this.angle !== undefined ) data.object.angle = this.angle;
+ if ( this.decay !== undefined ) data.object.decay = this.decay;
+ if ( this.exponent !== undefined ) data.object.exponent = this.exponent;
+
+ return data;
+
+};
+
+// File:src/lights/LightShadow.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.LightShadow = function ( camera ) {
+
+ this.camera = camera;
+
+ this.bias = 0;
+ this.darkness = 1;
+
+ this.mapSize = new THREE.Vector2( 512, 512 );
+
+ this.map = null;
+ this.matrix = null;
+
+};
+
+THREE.LightShadow.prototype = {
+
+ constructor: THREE.LightShadow,
+
+ copy: function ( source ) {
+
+ this.camera = source.camera.clone();
+
+ this.bias = source.bias;
+ this.darkness = source.darkness;
+
+ this.mapSize.copy( source.mapSize );
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ }
+
+};
+
+// File:src/lights/AmbientLight.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.AmbientLight = function ( color ) {
+
+ THREE.Light.call( this, color );
+
+ this.type = 'AmbientLight';
+
+ this.castShadow = undefined;
+
+};
+
+THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype );
+THREE.AmbientLight.prototype.constructor = THREE.AmbientLight;
+
+// File:src/lights/DirectionalLight.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.DirectionalLight = function ( color, intensity ) {
+
+ THREE.Light.call( this, color );
+
+ this.type = 'DirectionalLight';
+
+ this.position.set( 0, 1, 0 );
+ this.updateMatrix();
+
+ this.target = new THREE.Object3D();
+
+ this.intensity = ( intensity !== undefined ) ? intensity : 1;
+
+ this.shadow = new THREE.LightShadow( new THREE.OrthographicCamera( - 500, 500, 500, - 500, 50, 5000 ) );
+
+};
+
+THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype );
+THREE.DirectionalLight.prototype.constructor = THREE.DirectionalLight;
+
+THREE.DirectionalLight.prototype.copy = function ( source ) {
+
+ THREE.Light.prototype.copy.call( this, source );
+
+ this.intensity = source.intensity;
+ this.target = source.target.clone();
+
+ this.shadow = source.shadow.clone();
+
+ return this;
+
+};
+
+// File:src/lights/HemisphereLight.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) {
+
+ THREE.Light.call( this, skyColor );
+
+ this.type = 'HemisphereLight';
+
+ this.castShadow = undefined;
+
+ this.position.set( 0, 1, 0 );
+ this.updateMatrix();
+
+ this.groundColor = new THREE.Color( groundColor );
+ this.intensity = ( intensity !== undefined ) ? intensity : 1;
+
+};
+
+THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype );
+THREE.HemisphereLight.prototype.constructor = THREE.HemisphereLight;
+
+THREE.HemisphereLight.prototype.copy = function ( source ) {
+
+ THREE.Light.prototype.copy.call( this, source );
+
+ this.groundColor.copy( source.groundColor );
+ this.intensity = source.intensity;
+
+ return this;
+
+};
+
+// File:src/lights/PointLight.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+
+THREE.PointLight = function ( color, intensity, distance, decay ) {
+
+ THREE.Light.call( this, color );
+
+ this.type = 'PointLight';
+
+ this.intensity = ( intensity !== undefined ) ? intensity : 1;
+ this.distance = ( distance !== undefined ) ? distance : 0;
+ this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2.
+
+ this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 90, 1, 1, 500 ) );
+
+};
+
+THREE.PointLight.prototype = Object.create( THREE.Light.prototype );
+THREE.PointLight.prototype.constructor = THREE.PointLight;
+
+THREE.PointLight.prototype.copy = function ( source ) {
+
+ THREE.Light.prototype.copy.call( this, source );
+
+ this.intensity = source.intensity;
+ this.distance = source.distance;
+ this.decay = source.decay;
+
+ this.shadow = source.shadow.clone();
+
+ return this;
+
+};
+
+// File:src/lights/SpotLight.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SpotLight = function ( color, intensity, distance, angle, exponent, decay ) {
+
+ THREE.Light.call( this, color );
+
+ this.type = 'SpotLight';
+
+ this.position.set( 0, 1, 0 );
+ this.updateMatrix();
+
+ this.target = new THREE.Object3D();
+
+ this.intensity = ( intensity !== undefined ) ? intensity : 1;
+ this.distance = ( distance !== undefined ) ? distance : 0;
+ this.angle = ( angle !== undefined ) ? angle : Math.PI / 3;
+ this.exponent = ( exponent !== undefined ) ? exponent : 10;
+ this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2.
+
+ this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 50, 1, 50, 5000 ) );
+
+};
+
+THREE.SpotLight.prototype = Object.create( THREE.Light.prototype );
+THREE.SpotLight.prototype.constructor = THREE.SpotLight;
+
+THREE.SpotLight.prototype.copy = function ( source ) {
+
+ THREE.Light.prototype.copy.call( this, source );
+
+ this.intensity = source.intensity;
+ this.distance = source.distance;
+ this.angle = source.angle;
+ this.exponent = source.exponent;
+ this.decay = source.decay;
+
+ this.target = source.target.clone();
+
+ this.shadow = source.shadow.clone();
+
+ return this;
+
+};
+
+// File:src/loaders/Cache.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Cache = {
+
+ enabled: false,
+
+ files: {},
+
+ add: function ( key, file ) {
+
+ if ( this.enabled === false ) return;
+
+ // console.log( 'THREE.Cache', 'Adding key:', key );
+
+ this.files[ key ] = file;
+
+ },
+
+ get: function ( key ) {
+
+ if ( this.enabled === false ) return;
+
+ // console.log( 'THREE.Cache', 'Checking key:', key );
+
+ return this.files[ key ];
+
+ },
+
+ remove: function ( key ) {
+
+ delete this.files[ key ];
+
+ },
+
+ clear: function () {
+
+ this.files = {};
+
+ }
+
+};
+
+// File:src/loaders/Loader.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Loader = function () {
+
+ this.onLoadStart = function () {};
+ this.onLoadProgress = function () {};
+ this.onLoadComplete = function () {};
+
+};
+
+THREE.Loader.prototype = {
+
+ constructor: THREE.Loader,
+
+ crossOrigin: undefined,
+
+ extractUrlBase: function ( url ) {
+
+ var parts = url.split( '/' );
+
+ if ( parts.length === 1 ) return './';
+
+ parts.pop();
+
+ return parts.join( '/' ) + '/';
+
+ },
+
+ initMaterials: function ( materials, texturePath, crossOrigin ) {
+
+ var array = [];
+
+ for ( var i = 0; i < materials.length; ++ i ) {
+
+ array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin );
+
+ }
+
+ return array;
+
+ },
+
+ createMaterial: ( function () {
+
+ var color, textureLoader, materialLoader;
+
+ return function ( m, texturePath, crossOrigin ) {
+
+ if ( color === undefined ) color = new THREE.Color();
+ if ( textureLoader === undefined ) textureLoader = new THREE.TextureLoader();
+ if ( materialLoader === undefined ) materialLoader = new THREE.MaterialLoader();
+
+ // convert from old material format
+
+ var textures = {};
+
+ function loadTexture( path, repeat, offset, wrap, anisotropy ) {
+
+ var fullPath = texturePath + path;
+ var loader = THREE.Loader.Handlers.get( fullPath );
+
+ var texture;
+
+ if ( loader !== null ) {
+
+ texture = loader.load( fullPath );
+
+ } else {
+
+ textureLoader.setCrossOrigin( crossOrigin );
+ texture = textureLoader.load( fullPath );
+
+ }
+
+ if ( repeat !== undefined ) {
+
+ texture.repeat.fromArray( repeat );
+
+ if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping;
+ if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping;
+
+ }
+
+ if ( offset !== undefined ) {
+
+ texture.offset.fromArray( offset );
+
+ }
+
+ if ( wrap !== undefined ) {
+
+ if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = THREE.RepeatWrapping;
+ if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = THREE.MirroredRepeatWrapping;
+
+ if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = THREE.RepeatWrapping;
+ if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = THREE.MirroredRepeatWrapping;
+
+ }
+
+ if ( anisotropy !== undefined ) {
+
+ texture.anisotropy = anisotropy;
+
+ }
+
+ var uuid = THREE.Math.generateUUID();
+
+ textures[ uuid ] = texture;
+
+ return uuid;
+
+ }
+
+ //
+
+ var json = {
+ uuid: THREE.Math.generateUUID(),
+ type: 'MeshLambertMaterial'
+ };
+
+ for ( var name in m ) {
+
+ var value = m[ name ];
+
+ switch ( name ) {
+ case 'DbgColor':
+ json.color = value;
+ break;
+ case 'DbgIndex':
+ case 'opticalDensity':
+ case 'illumination':
+ // These were never supported
+ break;
+ case 'DbgName':
+ json.name = value;
+ break;
+ case 'blending':
+ json.blending = THREE[ value ];
+ break;
+ case 'colorDiffuse':
+ json.color = color.fromArray( value ).getHex();
+ break;
+ case 'colorSpecular':
+ json.specular = color.fromArray( value ).getHex();
+ break;
+ case 'colorEmissive':
+ json.emissive = color.fromArray( value ).getHex();
+ break;
+ case 'specularCoef':
+ json.shininess = value;
+ break;
+ case 'shading':
+ if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial';
+ if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial';
+ break;
+ case 'mapDiffuse':
+ json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy );
+ break;
+ case 'mapDiffuseRepeat':
+ case 'mapDiffuseOffset':
+ case 'mapDiffuseWrap':
+ case 'mapDiffuseAnisotropy':
+ break;
+ case 'mapLight':
+ json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy );
+ break;
+ case 'mapLightRepeat':
+ case 'mapLightOffset':
+ case 'mapLightWrap':
+ case 'mapLightAnisotropy':
+ break;
+ case 'mapAO':
+ json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy );
+ break;
+ case 'mapAORepeat':
+ case 'mapAOOffset':
+ case 'mapAOWrap':
+ case 'mapAOAnisotropy':
+ break;
+ case 'mapBump':
+ json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy );
+ break;
+ case 'mapBumpScale':
+ json.bumpScale = value;
+ break;
+ case 'mapBumpRepeat':
+ case 'mapBumpOffset':
+ case 'mapBumpWrap':
+ case 'mapBumpAnisotropy':
+ break;
+ case 'mapNormal':
+ json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy );
+ break;
+ case 'mapNormalFactor':
+ json.normalScale = [ value, value ];
+ break;
+ case 'mapNormalRepeat':
+ case 'mapNormalOffset':
+ case 'mapNormalWrap':
+ case 'mapNormalAnisotropy':
+ break;
+ case 'mapSpecular':
+ json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy );
+ break;
+ case 'mapSpecularRepeat':
+ case 'mapSpecularOffset':
+ case 'mapSpecularWrap':
+ case 'mapSpecularAnisotropy':
+ break;
+ case 'mapAlpha':
+ json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy );
+ break;
+ case 'mapAlphaRepeat':
+ case 'mapAlphaOffset':
+ case 'mapAlphaWrap':
+ case 'mapAlphaAnisotropy':
+ break;
+ case 'flipSided':
+ json.side = THREE.BackSide;
+ break;
+ case 'doubleSided':
+ json.side = THREE.DoubleSide;
+ break;
+ case 'transparency':
+ console.warn( 'THREE.Loader: transparency has been renamed to opacity' );
+ json.opacity = value;
+ break;
+ case 'opacity':
+ case 'transparent':
+ case 'depthTest':
+ case 'depthWrite':
+ case 'transparent':
+ case 'visible':
+ case 'wireframe':
+ json[ name ] = value;
+ break;
+ case 'vertexColors':
+ if ( value === true ) json.vertexColors = THREE.VertexColors;
+ if ( value === 'face' ) json.vertexColors = THREE.FaceColors;
+ break;
+ default:
+ console.error( 'Loader.createMaterial: Unsupported', name, value );
+ break;
+ }
+
+ }
+
+ if ( json.type !== 'MeshPhongMaterial' ) delete json.specular;
+ if ( json.opacity < 1 ) json.transparent = true;
+
+ materialLoader.setTextures( textures );
+
+ return materialLoader.parse( json );
+
+ };
+
+ } )()
+
+};
+
+THREE.Loader.Handlers = {
+
+ handlers: [],
+
+ add: function ( regex, loader ) {
+
+ this.handlers.push( regex, loader );
+
+ },
+
+ get: function ( file ) {
+
+ var handlers = this.handlers;
+
+ for ( var i = 0, l = handlers.length; i < l; i += 2 ) {
+
+ var regex = handlers[ i ];
+ var loader = handlers[ i + 1 ];
+
+ if ( regex.test( file ) ) {
+
+ return loader;
+
+ }
+
+ }
+
+ return null;
+
+ }
+
+};
+
+// File:src/loaders/XHRLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.XHRLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.XHRLoader.prototype = {
+
+ constructor: THREE.XHRLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var cached = THREE.Cache.get( url );
+
+ if ( cached !== undefined ) {
+
+ if ( onLoad ) {
+
+ setTimeout( function () {
+
+ onLoad( cached );
+
+ }, 0 );
+
+ }
+
+ return cached;
+
+ }
+
+ var request = new XMLHttpRequest();
+ request.open( 'GET', url, true );
+
+ request.addEventListener( 'load', function ( event ) {
+
+ var response = event.target.response;
+
+ THREE.Cache.add( url, response );
+
+ if ( onLoad ) onLoad( response );
+
+ scope.manager.itemEnd( url );
+
+ }, false );
+
+ if ( onProgress !== undefined ) {
+
+ request.addEventListener( 'progress', function ( event ) {
+
+ onProgress( event );
+
+ }, false );
+
+ }
+
+ request.addEventListener( 'error', function ( event ) {
+
+ if ( onError ) onError( event );
+
+ scope.manager.itemError( url );
+
+ }, false );
+
+ if ( this.crossOrigin !== undefined ) request.crossOrigin = this.crossOrigin;
+ if ( this.responseType !== undefined ) request.responseType = this.responseType;
+ if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials;
+
+ request.send( null );
+
+ scope.manager.itemStart( url );
+
+ return request;
+
+ },
+
+ setResponseType: function ( value ) {
+
+ this.responseType = value;
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ },
+
+ setWithCredentials: function ( value ) {
+
+ this.withCredentials = value;
+
+ }
+
+};
+
+// File:src/loaders/ImageLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.ImageLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.ImageLoader.prototype = {
+
+ constructor: THREE.ImageLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var cached = THREE.Cache.get( url );
+
+ if ( cached !== undefined ) {
+
+ scope.manager.itemStart( url );
+
+ if ( onLoad ) {
+
+ setTimeout( function () {
+
+ onLoad( cached );
+
+ scope.manager.itemEnd( url );
+
+ }, 0 );
+
+ } else {
+
+ scope.manager.itemEnd( url );
+
+ }
+
+ return cached;
+
+ }
+
+ var image = document.createElement( 'img' );
+
+ image.addEventListener( 'load', function ( event ) {
+
+ THREE.Cache.add( url, this );
+
+ if ( onLoad ) onLoad( this );
+
+ scope.manager.itemEnd( url );
+
+ }, false );
+
+ if ( onProgress !== undefined ) {
+
+ image.addEventListener( 'progress', function ( event ) {
+
+ onProgress( event );
+
+ }, false );
+
+ }
+
+ image.addEventListener( 'error', function ( event ) {
+
+ if ( onError ) onError( event );
+
+ scope.manager.itemError( url );
+
+ }, false );
+
+ if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin;
+
+ scope.manager.itemStart( url );
+
+ image.src = url;
+
+ return image;
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ }
+
+};
+
+// File:src/loaders/JSONLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.JSONLoader = function ( manager ) {
+
+ if ( typeof manager === 'boolean' ) {
+
+ console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' );
+ manager = undefined;
+
+ }
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+ this.withCredentials = false;
+
+};
+
+THREE.JSONLoader.prototype = {
+
+ constructor: THREE.JSONLoader,
+
+ // Deprecated
+
+ get statusDomElement () {
+
+ if ( this._statusDomElement === undefined ) {
+
+ this._statusDomElement = document.createElement( 'div' );
+
+ }
+
+ console.warn( 'THREE.JSONLoader: .statusDomElement has been removed.' );
+ return this._statusDomElement;
+
+ },
+
+ load: function( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : THREE.Loader.prototype.extractUrlBase( url );
+
+ var loader = new THREE.XHRLoader( this.manager );
+ loader.setCrossOrigin( this.crossOrigin );
+ loader.setWithCredentials( this.withCredentials );
+ loader.load( url, function ( text ) {
+
+ var json = JSON.parse( text );
+ var metadata = json.metadata;
+
+ if ( metadata !== undefined ) {
+
+ if ( metadata.type === 'object' ) {
+
+ console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' );
+ return;
+
+ }
+
+ if ( metadata.type === 'scene' ) {
+
+ console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' );
+ return;
+
+ }
+
+ }
+
+ var object = scope.parse( json, texturePath );
+ onLoad( object.geometry, object.materials );
+
+ } );
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ },
+
+ setTexturePath: function ( value ) {
+
+ this.texturePath = value;
+
+ },
+
+ parse: function ( json, texturePath ) {
+
+ var geometry = new THREE.Geometry(),
+ scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0;
+
+ parseModel( scale );
+
+ parseSkin();
+ parseMorphing( scale );
+ parseAnimations();
+
+ geometry.computeFaceNormals();
+ geometry.computeBoundingSphere();
+
+ function parseModel( scale ) {
+
+ function isBitSet( value, position ) {
+
+ return value & ( 1 << position );
+
+ }
+
+ var i, j, fi,
+
+ offset, zLength,
+
+ colorIndex, normalIndex, uvIndex, materialIndex,
+
+ type,
+ isQuad,
+ hasMaterial,
+ hasFaceVertexUv,
+ hasFaceNormal, hasFaceVertexNormal,
+ hasFaceColor, hasFaceVertexColor,
+
+ vertex, face, faceA, faceB, hex, normal,
+
+ uvLayer, uv, u, v,
+
+ faces = json.faces,
+ vertices = json.vertices,
+ normals = json.normals,
+ colors = json.colors,
+
+ nUvLayers = 0;
+
+ if ( json.uvs !== undefined ) {
+
+ // disregard empty arrays
+
+ for ( i = 0; i < json.uvs.length; i ++ ) {
+
+ if ( json.uvs[ i ].length ) nUvLayers ++;
+
+ }
+
+ for ( i = 0; i < nUvLayers; i ++ ) {
+
+ geometry.faceVertexUvs[ i ] = [];
+
+ }
+
+ }
+
+ offset = 0;
+ zLength = vertices.length;
+
+ while ( offset < zLength ) {
+
+ vertex = new THREE.Vector3();
+
+ vertex.x = vertices[ offset ++ ] * scale;
+ vertex.y = vertices[ offset ++ ] * scale;
+ vertex.z = vertices[ offset ++ ] * scale;
+
+ geometry.vertices.push( vertex );
+
+ }
+
+ offset = 0;
+ zLength = faces.length;
+
+ while ( offset < zLength ) {
+
+ type = faces[ offset ++ ];
+
+
+ isQuad = isBitSet( type, 0 );
+ hasMaterial = isBitSet( type, 1 );
+ hasFaceVertexUv = isBitSet( type, 3 );
+ hasFaceNormal = isBitSet( type, 4 );
+ hasFaceVertexNormal = isBitSet( type, 5 );
+ hasFaceColor = isBitSet( type, 6 );
+ hasFaceVertexColor = isBitSet( type, 7 );
+
+ // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);
+
+ if ( isQuad ) {
+
+ faceA = new THREE.Face3();
+ faceA.a = faces[ offset ];
+ faceA.b = faces[ offset + 1 ];
+ faceA.c = faces[ offset + 3 ];
+
+ faceB = new THREE.Face3();
+ faceB.a = faces[ offset + 1 ];
+ faceB.b = faces[ offset + 2 ];
+ faceB.c = faces[ offset + 3 ];
+
+ offset += 4;
+
+ if ( hasMaterial ) {
+
+ materialIndex = faces[ offset ++ ];
+ faceA.materialIndex = materialIndex;
+ faceB.materialIndex = materialIndex;
+
+ }
+
+ // to get face <=> uv index correspondence
+
+ fi = geometry.faces.length;
+
+ if ( hasFaceVertexUv ) {
+
+ for ( i = 0; i < nUvLayers; i ++ ) {
+
+ uvLayer = json.uvs[ i ];
+
+ geometry.faceVertexUvs[ i ][ fi ] = [];
+ geometry.faceVertexUvs[ i ][ fi + 1 ] = [];
+
+ for ( j = 0; j < 4; j ++ ) {
+
+ uvIndex = faces[ offset ++ ];
+
+ u = uvLayer[ uvIndex * 2 ];
+ v = uvLayer[ uvIndex * 2 + 1 ];
+
+ uv = new THREE.Vector2( u, v );
+
+ if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv );
+ if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv );
+
+ }
+
+ }
+
+ }
+
+ if ( hasFaceNormal ) {
+
+ normalIndex = faces[ offset ++ ] * 3;
+
+ faceA.normal.set(
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ]
+ );
+
+ faceB.normal.copy( faceA.normal );
+
+ }
+
+ if ( hasFaceVertexNormal ) {
+
+ for ( i = 0; i < 4; i ++ ) {
+
+ normalIndex = faces[ offset ++ ] * 3;
+
+ normal = new THREE.Vector3(
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ]
+ );
+
+
+ if ( i !== 2 ) faceA.vertexNormals.push( normal );
+ if ( i !== 0 ) faceB.vertexNormals.push( normal );
+
+ }
+
+ }
+
+
+ if ( hasFaceColor ) {
+
+ colorIndex = faces[ offset ++ ];
+ hex = colors[ colorIndex ];
+
+ faceA.color.setHex( hex );
+ faceB.color.setHex( hex );
+
+ }
+
+
+ if ( hasFaceVertexColor ) {
+
+ for ( i = 0; i < 4; i ++ ) {
+
+ colorIndex = faces[ offset ++ ];
+ hex = colors[ colorIndex ];
+
+ if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) );
+ if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) );
+
+ }
+
+ }
+
+ geometry.faces.push( faceA );
+ geometry.faces.push( faceB );
+
+ } else {
+
+ face = new THREE.Face3();
+ face.a = faces[ offset ++ ];
+ face.b = faces[ offset ++ ];
+ face.c = faces[ offset ++ ];
+
+ if ( hasMaterial ) {
+
+ materialIndex = faces[ offset ++ ];
+ face.materialIndex = materialIndex;
+
+ }
+
+ // to get face <=> uv index correspondence
+
+ fi = geometry.faces.length;
+
+ if ( hasFaceVertexUv ) {
+
+ for ( i = 0; i < nUvLayers; i ++ ) {
+
+ uvLayer = json.uvs[ i ];
+
+ geometry.faceVertexUvs[ i ][ fi ] = [];
+
+ for ( j = 0; j < 3; j ++ ) {
+
+ uvIndex = faces[ offset ++ ];
+
+ u = uvLayer[ uvIndex * 2 ];
+ v = uvLayer[ uvIndex * 2 + 1 ];
+
+ uv = new THREE.Vector2( u, v );
+
+ geometry.faceVertexUvs[ i ][ fi ].push( uv );
+
+ }
+
+ }
+
+ }
+
+ if ( hasFaceNormal ) {
+
+ normalIndex = faces[ offset ++ ] * 3;
+
+ face.normal.set(
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ]
+ );
+
+ }
+
+ if ( hasFaceVertexNormal ) {
+
+ for ( i = 0; i < 3; i ++ ) {
+
+ normalIndex = faces[ offset ++ ] * 3;
+
+ normal = new THREE.Vector3(
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ]
+ );
+
+ face.vertexNormals.push( normal );
+
+ }
+
+ }
+
+
+ if ( hasFaceColor ) {
+
+ colorIndex = faces[ offset ++ ];
+ face.color.setHex( colors[ colorIndex ] );
+
+ }
+
+
+ if ( hasFaceVertexColor ) {
+
+ for ( i = 0; i < 3; i ++ ) {
+
+ colorIndex = faces[ offset ++ ];
+ face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) );
+
+ }
+
+ }
+
+ geometry.faces.push( face );
+
+ }
+
+ }
+
+ };
+
+ function parseSkin() {
+
+ var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2;
+
+ if ( json.skinWeights ) {
+
+ for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) {
+
+ var x = json.skinWeights[ i ];
+ var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0;
+ var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0;
+ var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0;
+
+ geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) );
+
+ }
+
+ }
+
+ if ( json.skinIndices ) {
+
+ for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) {
+
+ var a = json.skinIndices[ i ];
+ var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0;
+ var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0;
+ var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0;
+
+ geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) );
+
+ }
+
+ }
+
+ geometry.bones = json.bones;
+
+ if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) {
+
+ console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' +
+ geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' );
+
+ }
+
+ };
+
+ function parseMorphing( scale ) {
+
+ if ( json.morphTargets !== undefined ) {
+
+ for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) {
+
+ geometry.morphTargets[ i ] = {};
+ geometry.morphTargets[ i ].name = json.morphTargets[ i ].name;
+ geometry.morphTargets[ i ].vertices = [];
+
+ var dstVertices = geometry.morphTargets[ i ].vertices;
+ var srcVertices = json.morphTargets[ i ].vertices;
+
+ for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) {
+
+ var vertex = new THREE.Vector3();
+ vertex.x = srcVertices[ v ] * scale;
+ vertex.y = srcVertices[ v + 1 ] * scale;
+ vertex.z = srcVertices[ v + 2 ] * scale;
+
+ dstVertices.push( vertex );
+
+ }
+
+ }
+
+ }
+
+ if ( json.morphColors !== undefined && json.morphColors.length > 0 ) {
+
+ console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' );
+
+ var faces = geometry.faces;
+ var morphColors = json.morphColors[ 0 ].colors;
+
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
+
+ faces[ i ].color.fromArray( morphColors, i * 3 );
+
+ }
+
+ }
+
+ }
+
+ function parseAnimations() {
+
+ var outputAnimations = [];
+
+ // parse old style Bone/Hierarchy animations
+ var animations = [];
+ if ( json.animation !== undefined ) {
+ animations.push( json.animation );
+ }
+ if ( json.animations !== undefined ) {
+ if ( json.animations.length ) {
+ animations = animations.concat( json.animations );
+ } else {
+ animations.push( json.animations );
+ }
+ }
+
+ for ( var i = 0; i < animations.length; i ++ ) {
+
+ var clip = THREE.AnimationClip.parseAnimation( animations[i], geometry.bones );
+ if ( clip ) outputAnimations.push( clip );
+
+ }
+
+ // parse implicit morph animations
+ if ( geometry.morphTargets ) {
+
+ // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary.
+ var morphAnimationClips = THREE.AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 );
+ outputAnimations = outputAnimations.concat( morphAnimationClips );
+
+ }
+
+ if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations;
+
+ };
+
+ if ( json.materials === undefined || json.materials.length === 0 ) {
+
+ return { geometry: geometry };
+
+ } else {
+
+ var materials = THREE.Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin );
+
+ return { geometry: geometry, materials: materials };
+
+ }
+
+ }
+
+};
+
+// File:src/loaders/LoadingManager.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.LoadingManager = function ( onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var isLoading = false, itemsLoaded = 0, itemsTotal = 0;
+
+ this.onStart = undefined;
+ this.onLoad = onLoad;
+ this.onProgress = onProgress;
+ this.onError = onError;
+
+ this.itemStart = function ( url ) {
+
+ itemsTotal ++;
+
+ if ( isLoading === false ) {
+
+ if ( scope.onStart !== undefined ) {
+
+ scope.onStart( url, itemsLoaded, itemsTotal );
+
+ }
+
+ }
+
+ isLoading = true;
+
+ };
+
+ this.itemEnd = function ( url ) {
+
+ itemsLoaded ++;
+
+ if ( scope.onProgress !== undefined ) {
+
+ scope.onProgress( url, itemsLoaded, itemsTotal );
+
+ }
+
+ if ( itemsLoaded === itemsTotal ) {
+
+ isLoading = false;
+
+ if ( scope.onLoad !== undefined ) {
+
+ scope.onLoad();
+
+ }
+
+ }
+
+ };
+
+ this.itemError = function ( url ) {
+
+ if ( scope.onError !== undefined ) {
+
+ scope.onError( url );
+
+ }
+
+ };
+
+};
+
+THREE.DefaultLoadingManager = new THREE.LoadingManager();
+
+// File:src/loaders/BufferGeometryLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.BufferGeometryLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.BufferGeometryLoader.prototype = {
+
+ constructor: THREE.BufferGeometryLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var loader = new THREE.XHRLoader( scope.manager );
+ loader.setCrossOrigin( this.crossOrigin );
+ loader.load( url, function ( text ) {
+
+ onLoad( scope.parse( JSON.parse( text ) ) );
+
+ }, onProgress, onError );
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ },
+
+ parse: function ( json ) {
+
+ var geometry = new THREE.BufferGeometry();
+
+ var index = json.data.index;
+
+ if ( index !== undefined ) {
+
+ var typedArray = new self[ index.type ]( index.array );
+ geometry.setIndex( new THREE.BufferAttribute( typedArray, 1 ) );
+
+ }
+
+ var attributes = json.data.attributes;
+
+ for ( var key in attributes ) {
+
+ var attribute = attributes[ key ];
+ var typedArray = new self[ attribute.type ]( attribute.array );
+
+ geometry.addAttribute( key, new THREE.BufferAttribute( typedArray, attribute.itemSize ) );
+
+ }
+
+ var groups = json.data.groups || json.data.drawcalls || json.data.offsets;
+
+ if ( groups !== undefined ) {
+
+ for ( var i = 0, n = groups.length; i !== n; ++ i ) {
+
+ var group = groups[ i ];
+
+ geometry.addGroup( group.start, group.count );
+
+ }
+
+ }
+
+ var boundingSphere = json.data.boundingSphere;
+
+ if ( boundingSphere !== undefined ) {
+
+ var center = new THREE.Vector3();
+
+ if ( boundingSphere.center !== undefined ) {
+
+ center.fromArray( boundingSphere.center );
+
+ }
+
+ geometry.boundingSphere = new THREE.Sphere( center, boundingSphere.radius );
+
+ }
+
+ return geometry;
+
+ }
+
+};
+
+// File:src/loaders/MaterialLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.MaterialLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+ this.textures = {};
+
+};
+
+THREE.MaterialLoader.prototype = {
+
+ constructor: THREE.MaterialLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var loader = new THREE.XHRLoader( scope.manager );
+ loader.setCrossOrigin( this.crossOrigin );
+ loader.load( url, function ( text ) {
+
+ onLoad( scope.parse( JSON.parse( text ) ) );
+
+ }, onProgress, onError );
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ },
+
+ setTextures: function ( value ) {
+
+ this.textures = value;
+
+ },
+
+ getTexture: function ( name ) {
+
+ var textures = this.textures;
+
+ if ( textures[ name ] === undefined ) {
+
+ console.warn( 'THREE.MaterialLoader: Undefined texture', name );
+
+ }
+
+ return textures[ name ];
+
+ },
+
+ parse: function ( json ) {
+
+ var material = new THREE[ json.type ];
+ material.uuid = json.uuid;
+
+ if ( json.name !== undefined ) material.name = json.name;
+ if ( json.color !== undefined ) material.color.setHex( json.color );
+ if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive );
+ if ( json.specular !== undefined ) material.specular.setHex( json.specular );
+ if ( json.shininess !== undefined ) material.shininess = json.shininess;
+ if ( json.uniforms !== undefined ) material.uniforms = json.uniforms;
+ if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader;
+ if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader;
+ if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors;
+ if ( json.shading !== undefined ) material.shading = json.shading;
+ if ( json.blending !== undefined ) material.blending = json.blending;
+ if ( json.side !== undefined ) material.side = json.side;
+ if ( json.opacity !== undefined ) material.opacity = json.opacity;
+ if ( json.transparent !== undefined ) material.transparent = json.transparent;
+ if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest;
+ if ( json.depthTest !== undefined ) material.depthTest = json.depthTest;
+ if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite;
+ if ( json.wireframe !== undefined ) material.wireframe = json.wireframe;
+ if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth;
+
+ // for PointsMaterial
+ if ( json.size !== undefined ) material.size = json.size;
+ if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation;
+
+ // maps
+
+ if ( json.map !== undefined ) material.map = this.getTexture( json.map );
+
+ if ( json.alphaMap !== undefined ) {
+
+ material.alphaMap = this.getTexture( json.alphaMap );
+ material.transparent = true;
+
+ }
+
+ if ( json.bumpMap !== undefined ) material.bumpMap = this.getTexture( json.bumpMap );
+ if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale;
+
+ if ( json.normalMap !== undefined ) material.normalMap = this.getTexture( json.normalMap );
+ if ( json.normalScale ) material.normalScale = new THREE.Vector2( json.normalScale, json.normalScale );
+
+ if ( json.displacementMap !== undefined ) material.displacementMap = this.getTexture( json.displacementMap );
+ if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale;
+ if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias;
+
+ if ( json.specularMap !== undefined ) material.specularMap = this.getTexture( json.specularMap );
+
+ if ( json.envMap !== undefined ) {
+
+ material.envMap = this.getTexture( json.envMap );
+ material.combine = THREE.MultiplyOperation;
+
+ }
+
+ if ( json.reflectivity ) material.reflectivity = json.reflectivity;
+
+ if ( json.lightMap !== undefined ) material.lightMap = this.getTexture( json.lightMap );
+ if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity;
+
+ if ( json.aoMap !== undefined ) material.aoMap = this.getTexture( json.aoMap );
+ if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity;
+
+ // MeshFaceMaterial
+
+ if ( json.materials !== undefined ) {
+
+ for ( var i = 0, l = json.materials.length; i < l; i ++ ) {
+
+ material.materials.push( this.parse( json.materials[ i ] ) );
+
+ }
+
+ }
+
+ return material;
+
+ }
+
+};
+
+// File:src/loaders/ObjectLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.ObjectLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+ this.texturePath = '';
+
+};
+
+THREE.ObjectLoader.prototype = {
+
+ constructor: THREE.ObjectLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ if ( this.texturePath === '' ) {
+
+ this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 );
+
+ }
+
+ var scope = this;
+
+ var loader = new THREE.XHRLoader( scope.manager );
+ loader.setCrossOrigin( this.crossOrigin );
+ loader.load( url, function ( text ) {
+
+ scope.parse( JSON.parse( text ), onLoad );
+
+ }, onProgress, onError );
+
+ },
+
+ setTexturePath: function ( value ) {
+
+ this.texturePath = value;
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ },
+
+ parse: function ( json, onLoad ) {
+
+ var geometries = this.parseGeometries( json.geometries );
+
+ var images = this.parseImages( json.images, function () {
+
+ if ( onLoad !== undefined ) onLoad( object );
+
+ } );
+
+ var textures = this.parseTextures( json.textures, images );
+ var materials = this.parseMaterials( json.materials, textures );
+
+ var object = this.parseObject( json.object, geometries, materials );
+
+ if ( json.animations ) {
+
+ object.animations = this.parseAnimations( json.animations );
+
+ }
+
+ if ( json.images === undefined || json.images.length === 0 ) {
+
+ if ( onLoad !== undefined ) onLoad( object );
+
+ }
+
+ return object;
+
+ },
+
+ parseGeometries: function ( json ) {
+
+ var geometries = {};
+
+ if ( json !== undefined ) {
+
+ var geometryLoader = new THREE.JSONLoader();
+ var bufferGeometryLoader = new THREE.BufferGeometryLoader();
+
+ for ( var i = 0, l = json.length; i < l; i ++ ) {
+
+ var geometry;
+ var data = json[ i ];
+
+ switch ( data.type ) {
+
+ case 'PlaneGeometry':
+ case 'PlaneBufferGeometry':
+
+ geometry = new THREE[ data.type ](
+ data.width,
+ data.height,
+ data.widthSegments,
+ data.heightSegments
+ );
+
+ break;
+
+ case 'BoxGeometry':
+ case 'CubeGeometry': // backwards compatible
+
+ geometry = new THREE.BoxGeometry(
+ data.width,
+ data.height,
+ data.depth,
+ data.widthSegments,
+ data.heightSegments,
+ data.depthSegments
+ );
+
+ break;
+
+ case 'CircleBufferGeometry':
+
+ geometry = new THREE.CircleBufferGeometry(
+ data.radius,
+ data.segments,
+ data.thetaStart,
+ data.thetaLength
+ );
+
+ break;
+
+ case 'CircleGeometry':
+
+ geometry = new THREE.CircleGeometry(
+ data.radius,
+ data.segments,
+ data.thetaStart,
+ data.thetaLength
+ );
+
+ break;
+
+ case 'CylinderGeometry':
+
+ geometry = new THREE.CylinderGeometry(
+ data.radiusTop,
+ data.radiusBottom,
+ data.height,
+ data.radialSegments,
+ data.heightSegments,
+ data.openEnded,
+ data.thetaStart,
+ data.thetaLength
+ );
+
+ break;
+
+ case 'SphereGeometry':
+
+ geometry = new THREE.SphereGeometry(
+ data.radius,
+ data.widthSegments,
+ data.heightSegments,
+ data.phiStart,
+ data.phiLength,
+ data.thetaStart,
+ data.thetaLength
+ );
+
+ break;
+
+ case 'SphereBufferGeometry':
+
+ geometry = new THREE.SphereBufferGeometry(
+ data.radius,
+ data.widthSegments,
+ data.heightSegments,
+ data.phiStart,
+ data.phiLength,
+ data.thetaStart,
+ data.thetaLength
+ );
+
+ break;
+
+ case 'DodecahedronGeometry':
+
+ geometry = new THREE.DodecahedronGeometry(
+ data.radius,
+ data.detail
+ );
+
+ break;
+
+ case 'IcosahedronGeometry':
+
+ geometry = new THREE.IcosahedronGeometry(
+ data.radius,
+ data.detail
+ );
+
+ break;
+
+ case 'OctahedronGeometry':
+
+ geometry = new THREE.OctahedronGeometry(
+ data.radius,
+ data.detail
+ );
+
+ break;
+
+ case 'TetrahedronGeometry':
+
+ geometry = new THREE.TetrahedronGeometry(
+ data.radius,
+ data.detail
+ );
+
+ break;
+
+ case 'RingGeometry':
+
+ geometry = new THREE.RingGeometry(
+ data.innerRadius,
+ data.outerRadius,
+ data.thetaSegments,
+ data.phiSegments,
+ data.thetaStart,
+ data.thetaLength
+ );
+
+ break;
+
+ case 'TorusGeometry':
+
+ geometry = new THREE.TorusGeometry(
+ data.radius,
+ data.tube,
+ data.radialSegments,
+ data.tubularSegments,
+ data.arc
+ );
+
+ break;
+
+ case 'TorusKnotGeometry':
+
+ geometry = new THREE.TorusKnotGeometry(
+ data.radius,
+ data.tube,
+ data.radialSegments,
+ data.tubularSegments,
+ data.p,
+ data.q,
+ data.heightScale
+ );
+
+ break;
+
+ case 'BufferGeometry':
+
+ geometry = bufferGeometryLoader.parse( data );
+
+ break;
+
+ case 'Geometry':
+
+ geometry = geometryLoader.parse( data.data, this.texturePath ).geometry;
+
+ break;
+
+ default:
+
+ console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' );
+
+ continue;
+
+ }
+
+ geometry.uuid = data.uuid;
+
+ if ( data.name !== undefined ) geometry.name = data.name;
+
+ geometries[ data.uuid ] = geometry;
+
+ }
+
+ }
+
+ return geometries;
+
+ },
+
+ parseMaterials: function ( json, textures ) {
+
+ var materials = {};
+
+ if ( json !== undefined ) {
+
+ var loader = new THREE.MaterialLoader();
+ loader.setTextures( textures );
+
+ for ( var i = 0, l = json.length; i < l; i ++ ) {
+
+ var material = loader.parse( json[ i ] );
+ materials[ material.uuid ] = material;
+
+ }
+
+ }
+
+ return materials;
+
+ },
+
+ parseAnimations: function ( json ) {
+
+ var animations = [];
+
+ for ( var i = 0; i < json.length; i ++ ) {
+
+ var clip = THREE.AnimationClip.parse( json[i] );
+
+ animations.push( clip );
+
+ }
+
+ return animations;
+
+ },
+
+ parseImages: function ( json, onLoad ) {
+
+ var scope = this;
+ var images = {};
+
+ function loadImage( url ) {
+
+ scope.manager.itemStart( url );
+
+ return loader.load( url, function () {
+
+ scope.manager.itemEnd( url );
+
+ } );
+
+ }
+
+ if ( json !== undefined && json.length > 0 ) {
+
+ var manager = new THREE.LoadingManager( onLoad );
+
+ var loader = new THREE.ImageLoader( manager );
+ loader.setCrossOrigin( this.crossOrigin );
+
+ for ( var i = 0, l = json.length; i < l; i ++ ) {
+
+ var image = json[ i ];
+ var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url;
+
+ images[ image.uuid ] = loadImage( path );
+
+ }
+
+ }
+
+ return images;
+
+ },
+
+ parseTextures: function ( json, images ) {
+
+ function parseConstant( value ) {
+
+ if ( typeof( value ) === 'number' ) return value;
+
+ console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value );
+
+ return THREE[ value ];
+
+ }
+
+ var textures = {};
+
+ if ( json !== undefined ) {
+
+ for ( var i = 0, l = json.length; i < l; i ++ ) {
+
+ var data = json[ i ];
+
+ if ( data.image === undefined ) {
+
+ console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid );
+
+ }
+
+ if ( images[ data.image ] === undefined ) {
+
+ console.warn( 'THREE.ObjectLoader: Undefined image', data.image );
+
+ }
+
+ var texture = new THREE.Texture( images[ data.image ] );
+ texture.needsUpdate = true;
+
+ texture.uuid = data.uuid;
+
+ if ( data.name !== undefined ) texture.name = data.name;
+ if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping );
+ if ( data.offset !== undefined ) texture.offset = new THREE.Vector2( data.offset[ 0 ], data.offset[ 1 ] );
+ if ( data.repeat !== undefined ) texture.repeat = new THREE.Vector2( data.repeat[ 0 ], data.repeat[ 1 ] );
+ if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter );
+ if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter );
+ if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy;
+ if ( Array.isArray( data.wrap ) ) {
+
+ texture.wrapS = parseConstant( data.wrap[ 0 ] );
+ texture.wrapT = parseConstant( data.wrap[ 1 ] );
+
+ }
+
+ textures[ data.uuid ] = texture;
+
+ }
+
+ }
+
+ return textures;
+
+ },
+
+ parseObject: function () {
+
+ var matrix = new THREE.Matrix4();
+
+ return function ( data, geometries, materials ) {
+
+ var object;
+
+ function getGeometry( name ) {
+
+ if ( geometries[ name ] === undefined ) {
+
+ console.warn( 'THREE.ObjectLoader: Undefined geometry', name );
+
+ }
+
+ return geometries[ name ];
+
+ }
+
+ function getMaterial( name ) {
+
+ if ( name === undefined ) return undefined;
+
+ if ( materials[ name ] === undefined ) {
+
+ console.warn( 'THREE.ObjectLoader: Undefined material', name );
+
+ }
+
+ return materials[ name ];
+
+ }
+
+ switch ( data.type ) {
+
+ case 'Scene':
+
+ object = new THREE.Scene();
+
+ break;
+
+ case 'PerspectiveCamera':
+
+ object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far );
+
+ break;
+
+ case 'OrthographicCamera':
+
+ object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far );
+
+ break;
+
+ case 'AmbientLight':
+
+ object = new THREE.AmbientLight( data.color );
+
+ break;
+
+ case 'DirectionalLight':
+
+ object = new THREE.DirectionalLight( data.color, data.intensity );
+
+ break;
+
+ case 'PointLight':
+
+ object = new THREE.PointLight( data.color, data.intensity, data.distance, data.decay );
+
+ break;
+
+ case 'SpotLight':
+
+ object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent, data.decay );
+
+ break;
+
+ case 'HemisphereLight':
+
+ object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity );
+
+ break;
+
+ case 'Mesh':
+
+ object = new THREE.Mesh( getGeometry( data.geometry ), getMaterial( data.material ) );
+
+ break;
+
+ case 'LOD':
+
+ object = new THREE.LOD();
+
+ break;
+
+ case 'Line':
+
+ object = new THREE.Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode );
+
+ break;
+
+ case 'PointCloud':
+ case 'Points':
+
+ object = new THREE.Points( getGeometry( data.geometry ), getMaterial( data.material ) );
+
+ break;
+
+ case 'Sprite':
+
+ object = new THREE.Sprite( getMaterial( data.material ) );
+
+ break;
+
+ case 'Group':
+
+ object = new THREE.Group();
+
+ break;
+
+ default:
+
+ object = new THREE.Object3D();
+
+ }
+
+ object.uuid = data.uuid;
+
+ if ( data.name !== undefined ) object.name = data.name;
+ if ( data.matrix !== undefined ) {
+
+ matrix.fromArray( data.matrix );
+ matrix.decompose( object.position, object.quaternion, object.scale );
+
+ } else {
+
+ if ( data.position !== undefined ) object.position.fromArray( data.position );
+ if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation );
+ if ( data.scale !== undefined ) object.scale.fromArray( data.scale );
+
+ }
+
+ if ( data.castShadow !== undefined ) object.castShadow = data.castShadow;
+ if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow;
+
+ if ( data.visible !== undefined ) object.visible = data.visible;
+ if ( data.userData !== undefined ) object.userData = data.userData;
+
+ if ( data.children !== undefined ) {
+
+ for ( var child in data.children ) {
+
+ object.add( this.parseObject( data.children[ child ], geometries, materials ) );
+
+ }
+
+ }
+
+ if ( data.type === 'LOD' ) {
+
+ var levels = data.levels;
+
+ for ( var l = 0; l < levels.length; l ++ ) {
+
+ var level = levels[ l ];
+ var child = object.getObjectByProperty( 'uuid', level.object );
+
+ if ( child !== undefined ) {
+
+ object.addLevel( child, level.distance );
+
+ }
+
+ }
+
+ }
+
+ return object;
+
+ }
+
+ }()
+
+};
+
+// File:src/loaders/TextureLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.TextureLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.TextureLoader.prototype = {
+
+ constructor: THREE.TextureLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var texture = new THREE.Texture();
+
+ var loader = new THREE.ImageLoader( this.manager );
+ loader.setCrossOrigin( this.crossOrigin );
+ loader.load( url, function ( image ) {
+
+ texture.image = image;
+ texture.needsUpdate = true;
+
+ if ( onLoad !== undefined ) {
+
+ onLoad( texture );
+
+ }
+
+ }, onProgress, onError );
+
+ return texture;
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ }
+
+};
+
+// File:src/loaders/CubeTextureLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.CubeTextureLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.CubeTextureLoader.prototype = {
+
+ constructor: THREE.CubeTextureLoader,
+
+ load: function ( urls, onLoad, onProgress, onError ) {
+
+ var texture = new THREE.CubeTexture( [] );
+
+ var loader = new THREE.ImageLoader();
+ loader.setCrossOrigin( this.crossOrigin );
+
+ var loaded = 0;
+
+ function loadTexture( i ) {
+
+ loader.load( urls[ i ], function ( image ) {
+
+ texture.images[ i ] = image;
+
+ loaded ++;
+
+ if ( loaded === 6 ) {
+
+ texture.needsUpdate = true;
+
+ if ( onLoad ) onLoad( texture );
+
+ }
+
+ }, undefined, onError );
+
+ }
+
+ for ( var i = 0; i < urls.length; ++ i ) {
+
+ loadTexture( i );
+
+ }
+
+ return texture;
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ }
+
+};
+
+// File:src/loaders/BinaryTextureLoader.js
+
+/**
+ * @author Nikos M. / https://github.com/foo123/
+ *
+ * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...)
+ */
+
+THREE.DataTextureLoader = THREE.BinaryTextureLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+ // override in sub classes
+ this._parser = null;
+
+};
+
+THREE.BinaryTextureLoader.prototype = {
+
+ constructor: THREE.BinaryTextureLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var texture = new THREE.DataTexture();
+
+ var loader = new THREE.XHRLoader( this.manager );
+ loader.setCrossOrigin( this.crossOrigin );
+ loader.setResponseType( 'arraybuffer' );
+
+ loader.load( url, function ( buffer ) {
+
+ var texData = scope._parser( buffer );
+
+ if ( ! texData ) return;
+
+ if ( undefined !== texData.image ) {
+
+ texture.image = texData.image;
+
+ } else if ( undefined !== texData.data ) {
+
+ texture.image.width = texData.width;
+ texture.image.height = texData.height;
+ texture.image.data = texData.data;
+
+ }
+
+ texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : THREE.ClampToEdgeWrapping;
+ texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : THREE.ClampToEdgeWrapping;
+
+ texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : THREE.LinearFilter;
+ texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : THREE.LinearMipMapLinearFilter;
+
+ texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1;
+
+ if ( undefined !== texData.format ) {
+
+ texture.format = texData.format;
+
+ }
+ if ( undefined !== texData.type ) {
+
+ texture.type = texData.type;
+
+ }
+
+ if ( undefined !== texData.mipmaps ) {
+
+ texture.mipmaps = texData.mipmaps;
+
+ }
+
+ if ( 1 === texData.mipmapCount ) {
+
+ texture.minFilter = THREE.LinearFilter;
+
+ }
+
+ texture.needsUpdate = true;
+
+ if ( onLoad ) onLoad( texture, texData );
+
+ }, onProgress, onError );
+
+
+ return texture;
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ }
+
+};
+
+// File:src/loaders/CompressedTextureLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ *
+ * Abstract Base class to block based textures loader (dds, pvr, ...)
+ */
+
+THREE.CompressedTextureLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+ // override in sub classes
+ this._parser = null;
+
+};
+
+
+THREE.CompressedTextureLoader.prototype = {
+
+ constructor: THREE.CompressedTextureLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var images = [];
+
+ var texture = new THREE.CompressedTexture();
+ texture.image = images;
+
+ var loader = new THREE.XHRLoader( this.manager );
+ loader.setCrossOrigin( this.crossOrigin );
+ loader.setResponseType( 'arraybuffer' );
+
+ if ( Array.isArray( url ) ) {
+
+ var loaded = 0;
+
+ var loadTexture = function ( i ) {
+
+ loader.load( url[ i ], function ( buffer ) {
+
+ var texDatas = scope._parser( buffer, true );
+
+ images[ i ] = {
+ width: texDatas.width,
+ height: texDatas.height,
+ format: texDatas.format,
+ mipmaps: texDatas.mipmaps
+ };
+
+ loaded += 1;
+
+ if ( loaded === 6 ) {
+
+ if ( texDatas.mipmapCount === 1 )
+ texture.minFilter = THREE.LinearFilter;
+
+ texture.format = texDatas.format;
+ texture.needsUpdate = true;
+
+ if ( onLoad ) onLoad( texture );
+
+ }
+
+ }, onProgress, onError );
+
+ };
+
+ for ( var i = 0, il = url.length; i < il; ++ i ) {
+
+ loadTexture( i );
+
+ }
+
+ } else {
+
+ // compressed cubemap texture stored in a single DDS file
+
+ loader.load( url, function ( buffer ) {
+
+ var texDatas = scope._parser( buffer, true );
+
+ if ( texDatas.isCubemap ) {
+
+ var faces = texDatas.mipmaps.length / texDatas.mipmapCount;
+
+ for ( var f = 0; f < faces; f ++ ) {
+
+ images[ f ] = { mipmaps : [] };
+
+ for ( var i = 0; i < texDatas.mipmapCount; i ++ ) {
+
+ images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] );
+ images[ f ].format = texDatas.format;
+ images[ f ].width = texDatas.width;
+ images[ f ].height = texDatas.height;
+
+ }
+
+ }
+
+ } else {
+
+ texture.image.width = texDatas.width;
+ texture.image.height = texDatas.height;
+ texture.mipmaps = texDatas.mipmaps;
+
+ }
+
+ if ( texDatas.mipmapCount === 1 ) {
+
+ texture.minFilter = THREE.LinearFilter;
+
+ }
+
+ texture.format = texDatas.format;
+ texture.needsUpdate = true;
+
+ if ( onLoad ) onLoad( texture );
+
+ }, onProgress, onError );
+
+ }
+
+ return texture;
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ }
+
+};
+
+// File:src/materials/Material.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Material = function () {
+
+ Object.defineProperty( this, 'id', { value: THREE.MaterialIdCount ++ } );
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.name = '';
+ this.type = 'Material';
+
+ this.side = THREE.FrontSide;
+
+ this.opacity = 1;
+ this.transparent = false;
+
+ this.blending = THREE.NormalBlending;
+
+ this.blendSrc = THREE.SrcAlphaFactor;
+ this.blendDst = THREE.OneMinusSrcAlphaFactor;
+ this.blendEquation = THREE.AddEquation;
+ this.blendSrcAlpha = null;
+ this.blendDstAlpha = null;
+ this.blendEquationAlpha = null;
+
+ this.depthFunc = THREE.LessEqualDepth;
+ this.depthTest = true;
+ this.depthWrite = true;
+
+ this.colorWrite = true;
+
+ this.precision = null; // override the renderer's default precision for this material
+
+ this.polygonOffset = false;
+ this.polygonOffsetFactor = 0;
+ this.polygonOffsetUnits = 0;
+
+ this.alphaTest = 0;
+
+ this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer
+
+ this.visible = true;
+
+ this._needsUpdate = true;
+
+};
+
+THREE.Material.prototype = {
+
+ constructor: THREE.Material,
+
+ get needsUpdate () {
+
+ return this._needsUpdate;
+
+ },
+
+ set needsUpdate ( value ) {
+
+ if ( value === true ) this.update();
+
+ this._needsUpdate = value;
+
+ },
+
+ setValues: function ( values ) {
+
+ if ( values === undefined ) return;
+
+ for ( var key in values ) {
+
+ var newValue = values[ key ];
+
+ if ( newValue === undefined ) {
+
+ console.warn( "THREE.Material: '" + key + "' parameter is undefined." );
+ continue;
+
+ }
+
+ var currentValue = this[ key ];
+
+ if ( currentValue === undefined ) {
+
+ console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." );
+ continue;
+
+ }
+
+ if ( currentValue instanceof THREE.Color ) {
+
+ currentValue.set( newValue );
+
+ } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) {
+
+ currentValue.copy( newValue );
+
+ } else if ( key === 'overdraw' ) {
+
+ // ensure overdraw is backwards-compatible with legacy boolean type
+ this[ key ] = Number( newValue );
+
+ } else {
+
+ this[ key ] = newValue;
+
+ }
+
+ }
+
+ },
+
+ toJSON: function ( meta ) {
+
+ var data = {
+ metadata: {
+ version: 4.4,
+ type: 'Material',
+ generator: 'Material.toJSON'
+ }
+ };
+
+ // standard Material serialization
+ data.uuid = this.uuid;
+ data.type = this.type;
+ if ( this.name !== '' ) data.name = this.name;
+
+ if ( this.color instanceof THREE.Color ) data.color = this.color.getHex();
+ if ( this.emissive instanceof THREE.Color ) data.emissive = this.emissive.getHex();
+ if ( this.specular instanceof THREE.Color ) data.specular = this.specular.getHex();
+ if ( this.shininess !== undefined ) data.shininess = this.shininess;
+
+ if ( this.map instanceof THREE.Texture ) data.map = this.map.toJSON( meta ).uuid;
+ if ( this.alphaMap instanceof THREE.Texture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid;
+ if ( this.lightMap instanceof THREE.Texture ) data.lightMap = this.lightMap.toJSON( meta ).uuid;
+ if ( this.bumpMap instanceof THREE.Texture ) {
+
+ data.bumpMap = this.bumpMap.toJSON( meta ).uuid;
+ data.bumpScale = this.bumpScale;
+
+ }
+ if ( this.normalMap instanceof THREE.Texture ) {
+
+ data.normalMap = this.normalMap.toJSON( meta ).uuid;
+ data.normalScale = this.normalScale; // Removed for now, causes issue in editor ui.js
+
+ }
+ if ( this.displacementMap instanceof THREE.Texture ) {
+
+ data.displacementMap = this.displacementMap.toJSON( meta ).uuid;
+ data.displacementScale = this.displacementScale;
+ data.displacementBias = this.displacementBias;
+
+ }
+ if ( this.specularMap instanceof THREE.Texture ) data.specularMap = this.specularMap.toJSON( meta ).uuid;
+ if ( this.envMap instanceof THREE.Texture ) {
+
+ data.envMap = this.envMap.toJSON( meta ).uuid;
+ data.reflectivity = this.reflectivity; // Scale behind envMap
+
+ }
+
+ if ( this.size !== undefined ) data.size = this.size;
+ if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;
+
+ if ( this.vertexColors !== undefined && this.vertexColors !== THREE.NoColors ) data.vertexColors = this.vertexColors;
+ if ( this.shading !== undefined && this.shading !== THREE.SmoothShading ) data.shading = this.shading;
+ if ( this.blending !== undefined && this.blending !== THREE.NormalBlending ) data.blending = this.blending;
+ if ( this.side !== undefined && this.side !== THREE.FrontSide ) data.side = this.side;
+
+ if ( this.opacity < 1 ) data.opacity = this.opacity;
+ if ( this.transparent === true ) data.transparent = this.transparent;
+ if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;
+ if ( this.wireframe === true ) data.wireframe = this.wireframe;
+ if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;
+
+ return data;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.name = source.name;
+
+ this.side = source.side;
+
+ this.opacity = source.opacity;
+ this.transparent = source.transparent;
+
+ this.blending = source.blending;
+
+ this.blendSrc = source.blendSrc;
+ this.blendDst = source.blendDst;
+ this.blendEquation = source.blendEquation;
+ this.blendSrcAlpha = source.blendSrcAlpha;
+ this.blendDstAlpha = source.blendDstAlpha;
+ this.blendEquationAlpha = source.blendEquationAlpha;
+
+ this.depthFunc = source.depthFunc;
+ this.depthTest = source.depthTest;
+ this.depthWrite = source.depthWrite;
+
+ this.precision = source.precision;
+
+ this.polygonOffset = source.polygonOffset;
+ this.polygonOffsetFactor = source.polygonOffsetFactor;
+ this.polygonOffsetUnits = source.polygonOffsetUnits;
+
+ this.alphaTest = source.alphaTest;
+
+ this.overdraw = source.overdraw;
+
+ this.visible = source.visible;
+
+ return this;
+
+ },
+
+ update: function () {
+
+ this.dispatchEvent( { type: 'update' } );
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ },
+
+ // Deprecated
+
+ get wrapAround () {
+
+ console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' );
+
+ },
+
+ set wrapAround ( boolean ) {
+
+ console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' );
+
+ },
+
+ get wrapRGB () {
+
+ console.warn( 'THREE.' + this.type + ': .wrapRGB has been removed.' );
+ return new THREE.Color();
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.Material.prototype );
+
+THREE.MaterialIdCount = 0;
+
+// File:src/materials/LineBasicMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * color: ,
+ * opacity: ,
+ *
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * linewidth: ,
+ * linecap: "round",
+ * linejoin: "round",
+ *
+ * vertexColors:
+ *
+ * fog:
+ * }
+ */
+
+THREE.LineBasicMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'LineBasicMaterial';
+
+ this.color = new THREE.Color( 0xffffff );
+
+ this.linewidth = 1;
+ this.linecap = 'round';
+ this.linejoin = 'round';
+
+ this.vertexColors = THREE.NoColors;
+
+ this.fog = true;
+
+ this.setValues( parameters );
+
+};
+
+THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.LineBasicMaterial.prototype.constructor = THREE.LineBasicMaterial;
+
+THREE.LineBasicMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+
+ this.linewidth = source.linewidth;
+ this.linecap = source.linecap;
+ this.linejoin = source.linejoin;
+
+ this.vertexColors = source.vertexColors;
+
+ this.fog = source.fog;
+
+ return this;
+
+};
+
+// File:src/materials/LineDashedMaterial.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * color: ,
+ * opacity: ,
+ *
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * linewidth: ,
+ *
+ * scale: ,
+ * dashSize: ,
+ * gapSize: ,
+ *
+ * vertexColors:
+ *
+ * fog:
+ * }
+ */
+
+THREE.LineDashedMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'LineDashedMaterial';
+
+ this.color = new THREE.Color( 0xffffff );
+
+ this.linewidth = 1;
+
+ this.scale = 1;
+ this.dashSize = 3;
+ this.gapSize = 1;
+
+ this.vertexColors = false;
+
+ this.fog = true;
+
+ this.setValues( parameters );
+
+};
+
+THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.LineDashedMaterial.prototype.constructor = THREE.LineDashedMaterial;
+
+THREE.LineDashedMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+
+ this.linewidth = source.linewidth;
+
+ this.scale = source.scale;
+ this.dashSize = source.dashSize;
+ this.gapSize = source.gapSize;
+
+ this.vertexColors = source.vertexColors;
+
+ this.fog = source.fog;
+
+ return this;
+
+};
+
+// File:src/materials/MeshBasicMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * color: ,
+ * opacity: ,
+ * map: new THREE.Texture( ),
+ *
+ * aoMap: new THREE.Texture( ),
+ * aoMapIntensity:
+ *
+ * specularMap: new THREE.Texture( ),
+ *
+ * alphaMap: new THREE.Texture( ),
+ *
+ * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
+ * combine: THREE.Multiply,
+ * reflectivity: ,
+ * refractionRatio: ,
+ *
+ * shading: THREE.SmoothShading,
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * wireframe: ,
+ * wireframeLinewidth: ,
+ *
+ * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
+ *
+ * skinning: ,
+ * morphTargets: ,
+ *
+ * fog:
+ * }
+ */
+
+THREE.MeshBasicMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'MeshBasicMaterial';
+
+ this.color = new THREE.Color( 0xffffff ); // emissive
+
+ this.map = null;
+
+ this.aoMap = null;
+ this.aoMapIntensity = 1.0;
+
+ this.specularMap = null;
+
+ this.alphaMap = null;
+
+ this.envMap = null;
+ this.combine = THREE.MultiplyOperation;
+ this.reflectivity = 1;
+ this.refractionRatio = 0.98;
+
+ this.fog = true;
+
+ this.shading = THREE.SmoothShading;
+
+ this.wireframe = false;
+ this.wireframeLinewidth = 1;
+ this.wireframeLinecap = 'round';
+ this.wireframeLinejoin = 'round';
+
+ this.vertexColors = THREE.NoColors;
+
+ this.skinning = false;
+ this.morphTargets = false;
+
+ this.setValues( parameters );
+
+};
+
+THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshBasicMaterial.prototype.constructor = THREE.MeshBasicMaterial;
+
+THREE.MeshBasicMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+
+ this.map = source.map;
+
+ this.aoMap = source.aoMap;
+ this.aoMapIntensity = source.aoMapIntensity;
+
+ this.specularMap = source.specularMap;
+
+ this.alphaMap = source.alphaMap;
+
+ this.envMap = source.envMap;
+ this.combine = source.combine;
+ this.reflectivity = source.reflectivity;
+ this.refractionRatio = source.refractionRatio;
+
+ this.fog = source.fog;
+
+ this.shading = source.shading;
+
+ this.wireframe = source.wireframe;
+ this.wireframeLinewidth = source.wireframeLinewidth;
+ this.wireframeLinecap = source.wireframeLinecap;
+ this.wireframeLinejoin = source.wireframeLinejoin;
+
+ this.vertexColors = source.vertexColors;
+
+ this.skinning = source.skinning;
+ this.morphTargets = source.morphTargets;
+
+ return this;
+
+};
+
+// File:src/materials/MeshLambertMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * color: ,
+ * emissive: ,
+ * opacity: ,
+ *
+ * map: new THREE.Texture( ),
+ *
+ * specularMap: new THREE.Texture( ),
+ *
+ * alphaMap: new THREE.Texture( ),
+ *
+ * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
+ * combine: THREE.Multiply,
+ * reflectivity: ,
+ * refractionRatio: ,
+ *
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * wireframe: ,
+ * wireframeLinewidth: ,
+ *
+ * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
+ *
+ * skinning: ,
+ * morphTargets: ,
+ * morphNormals: ,
+ *
+ * fog:
+ * }
+ */
+
+THREE.MeshLambertMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'MeshLambertMaterial';
+
+ this.color = new THREE.Color( 0xffffff ); // diffuse
+ this.emissive = new THREE.Color( 0x000000 );
+
+ this.map = null;
+
+ this.specularMap = null;
+
+ this.alphaMap = null;
+
+ this.envMap = null;
+ this.combine = THREE.MultiplyOperation;
+ this.reflectivity = 1;
+ this.refractionRatio = 0.98;
+
+ this.fog = true;
+
+ this.wireframe = false;
+ this.wireframeLinewidth = 1;
+ this.wireframeLinecap = 'round';
+ this.wireframeLinejoin = 'round';
+
+ this.vertexColors = THREE.NoColors;
+
+ this.skinning = false;
+ this.morphTargets = false;
+ this.morphNormals = false;
+
+ this.setValues( parameters );
+
+};
+
+THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshLambertMaterial.prototype.constructor = THREE.MeshLambertMaterial;
+
+THREE.MeshLambertMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+ this.emissive.copy( source.emissive );
+
+ this.map = source.map;
+
+ this.specularMap = source.specularMap;
+
+ this.alphaMap = source.alphaMap;
+
+ this.envMap = source.envMap;
+ this.combine = source.combine;
+ this.reflectivity = source.reflectivity;
+ this.refractionRatio = source.refractionRatio;
+
+ this.fog = source.fog;
+
+ this.wireframe = source.wireframe;
+ this.wireframeLinewidth = source.wireframeLinewidth;
+ this.wireframeLinecap = source.wireframeLinecap;
+ this.wireframeLinejoin = source.wireframeLinejoin;
+
+ this.vertexColors = source.vertexColors;
+
+ this.skinning = source.skinning;
+ this.morphTargets = source.morphTargets;
+ this.morphNormals = source.morphNormals;
+
+ return this;
+
+};
+
+// File:src/materials/MeshPhongMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * color: ,
+ * emissive: ,
+ * specular: ,
+ * shininess: ,
+ * opacity: ,
+ *
+ * map: new THREE.Texture( ),
+ *
+ * lightMap: new THREE.Texture( ),
+ * lightMapIntensity:
+ *
+ * aoMap: new THREE.Texture( ),
+ * aoMapIntensity:
+ *
+ * emissiveMap: new THREE.Texture( ),
+ *
+ * bumpMap: new THREE.Texture( ),
+ * bumpScale: ,
+ *
+ * normalMap: new THREE.Texture( ),
+ * normalScale: ,
+ *
+ * displacementMap: new THREE.Texture( ),
+ * displacementScale: ,
+ * displacementBias: ,
+ *
+ * specularMap: new THREE.Texture( ),
+ *
+ * alphaMap: new THREE.Texture( ),
+ *
+ * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
+ * combine: THREE.Multiply,
+ * reflectivity: ,
+ * refractionRatio: ,
+ *
+ * shading: THREE.SmoothShading,
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * wireframe: ,
+ * wireframeLinewidth: ,
+ *
+ * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
+ *
+ * skinning: ,
+ * morphTargets: ,
+ * morphNormals: ,
+ *
+ * fog:
+ * }
+ */
+
+THREE.MeshPhongMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'MeshPhongMaterial';
+
+ this.color = new THREE.Color( 0xffffff ); // diffuse
+ this.emissive = new THREE.Color( 0x000000 );
+ this.specular = new THREE.Color( 0x111111 );
+ this.shininess = 30;
+
+ this.metal = false;
+
+ this.map = null;
+
+ this.lightMap = null;
+ this.lightMapIntensity = 1.0;
+
+ this.aoMap = null;
+ this.aoMapIntensity = 1.0;
+
+ this.emissiveMap = null;
+
+ this.bumpMap = null;
+ this.bumpScale = 1;
+
+ this.normalMap = null;
+ this.normalScale = new THREE.Vector2( 1, 1 );
+
+ this.displacementMap = null;
+ this.displacementScale = 1;
+ this.displacementBias = 0;
+
+ this.specularMap = null;
+
+ this.alphaMap = null;
+
+ this.envMap = null;
+ this.combine = THREE.MultiplyOperation;
+ this.reflectivity = 1;
+ this.refractionRatio = 0.98;
+
+ this.fog = true;
+
+ this.shading = THREE.SmoothShading;
+
+ this.wireframe = false;
+ this.wireframeLinewidth = 1;
+ this.wireframeLinecap = 'round';
+ this.wireframeLinejoin = 'round';
+
+ this.vertexColors = THREE.NoColors;
+
+ this.skinning = false;
+ this.morphTargets = false;
+ this.morphNormals = false;
+
+ this.setValues( parameters );
+
+};
+
+THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshPhongMaterial.prototype.constructor = THREE.MeshPhongMaterial;
+
+THREE.MeshPhongMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+ this.emissive.copy( source.emissive );
+ this.specular.copy( source.specular );
+ this.shininess = source.shininess;
+
+ this.metal = source.metal;
+
+ this.map = source.map;
+
+ this.lightMap = source.lightMap;
+ this.lightMapIntensity = source.lightMapIntensity;
+
+ this.aoMap = source.aoMap;
+ this.aoMapIntensity = source.aoMapIntensity;
+
+ this.emissiveMap = source.emissiveMap;
+
+ this.bumpMap = source.bumpMap;
+ this.bumpScale = source.bumpScale;
+
+ this.normalMap = source.normalMap;
+ this.normalScale.copy( source.normalScale );
+
+ this.displacementMap = source.displacementMap;
+ this.displacementScale = source.displacementScale;
+ this.displacementBias = source.displacementBias;
+
+ this.specularMap = source.specularMap;
+
+ this.alphaMap = source.alphaMap;
+
+ this.envMap = source.envMap;
+ this.combine = source.combine;
+ this.reflectivity = source.reflectivity;
+ this.refractionRatio = source.refractionRatio;
+
+ this.fog = source.fog;
+
+ this.shading = source.shading;
+
+ this.wireframe = source.wireframe;
+ this.wireframeLinewidth = source.wireframeLinewidth;
+ this.wireframeLinecap = source.wireframeLinecap;
+ this.wireframeLinejoin = source.wireframeLinejoin;
+
+ this.vertexColors = source.vertexColors;
+
+ this.skinning = source.skinning;
+ this.morphTargets = source.morphTargets;
+ this.morphNormals = source.morphNormals;
+
+ return this;
+
+};
+
+// File:src/materials/MeshDepthMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * opacity: ,
+ *
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * wireframe: ,
+ * wireframeLinewidth:
+ * }
+ */
+
+THREE.MeshDepthMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'MeshDepthMaterial';
+
+ this.morphTargets = false;
+ this.wireframe = false;
+ this.wireframeLinewidth = 1;
+
+ this.setValues( parameters );
+
+};
+
+THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshDepthMaterial.prototype.constructor = THREE.MeshDepthMaterial;
+
+THREE.MeshDepthMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.wireframe = source.wireframe;
+ this.wireframeLinewidth = source.wireframeLinewidth;
+
+ return this;
+
+};
+
+// File:src/materials/MeshNormalMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ *
+ * parameters = {
+ * opacity: ,
+ *
+ * shading: THREE.FlatShading,
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * wireframe: ,
+ * wireframeLinewidth:
+ * }
+ */
+
+THREE.MeshNormalMaterial = function ( parameters ) {
+
+ THREE.Material.call( this, parameters );
+
+ this.type = 'MeshNormalMaterial';
+
+ this.wireframe = false;
+ this.wireframeLinewidth = 1;
+
+ this.morphTargets = false;
+
+ this.setValues( parameters );
+
+};
+
+THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshNormalMaterial.prototype.constructor = THREE.MeshNormalMaterial;
+
+THREE.MeshNormalMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.wireframe = source.wireframe;
+ this.wireframeLinewidth = source.wireframeLinewidth;
+
+ return this;
+
+};
+
+// File:src/materials/MultiMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.MultiMaterial = function ( materials ) {
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.type = 'MultiMaterial';
+
+ this.materials = materials instanceof Array ? materials : [];
+
+ this.visible = true;
+
+};
+
+THREE.MultiMaterial.prototype = {
+
+ constructor: THREE.MultiMaterial,
+
+ toJSON: function () {
+
+ var output = {
+ metadata: {
+ version: 4.2,
+ type: 'material',
+ generator: 'MaterialExporter'
+ },
+ uuid: this.uuid,
+ type: this.type,
+ materials: []
+ };
+
+ for ( var i = 0, l = this.materials.length; i < l; i ++ ) {
+
+ output.materials.push( this.materials[ i ].toJSON() );
+
+ }
+
+ output.visible = this.visible;
+
+ return output;
+
+ },
+
+ clone: function () {
+
+ var material = new this.constructor();
+
+ for ( var i = 0; i < this.materials.length; i ++ ) {
+
+ material.materials.push( this.materials[ i ].clone() );
+
+ }
+
+ material.visible = this.visible;
+
+ return material;
+
+ }
+
+};
+
+// backwards compatibility
+
+THREE.MeshFaceMaterial = THREE.MultiMaterial;
+
+// File:src/materials/PointsMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * color: ,
+ * opacity: ,
+ * map: new THREE.Texture( ),
+ *
+ * size: ,
+ * sizeAttenuation: ,
+ *
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * vertexColors: ,
+ *
+ * fog:
+ * }
+ */
+
+THREE.PointsMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'PointsMaterial';
+
+ this.color = new THREE.Color( 0xffffff );
+
+ this.map = null;
+
+ this.size = 1;
+ this.sizeAttenuation = true;
+
+ this.vertexColors = THREE.NoColors;
+
+ this.fog = true;
+
+ this.setValues( parameters );
+
+};
+
+THREE.PointsMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.PointsMaterial.prototype.constructor = THREE.PointsMaterial;
+
+THREE.PointsMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+
+ this.map = source.map;
+
+ this.size = source.size;
+ this.sizeAttenuation = source.sizeAttenuation;
+
+ this.vertexColors = source.vertexColors;
+
+ this.fog = source.fog;
+
+ return this;
+
+};
+
+// backwards compatibility
+
+THREE.PointCloudMaterial = function ( parameters ) {
+
+ console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' );
+ return new THREE.PointsMaterial( parameters );
+
+};
+
+THREE.ParticleBasicMaterial = function ( parameters ) {
+
+ console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' );
+ return new THREE.PointsMaterial( parameters );
+
+};
+
+THREE.ParticleSystemMaterial = function ( parameters ) {
+
+ console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' );
+ return new THREE.PointsMaterial( parameters );
+
+};
+
+// File:src/materials/ShaderMaterial.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * defines: { "label" : "value" },
+ * uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } },
+ *
+ * fragmentShader: ,
+ * vertexShader: ,
+ *
+ * shading: THREE.SmoothShading,
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * wireframe: ,
+ * wireframeLinewidth: ,
+ *
+ * lights: ,
+ *
+ * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
+ *
+ * skinning: ,
+ * morphTargets: ,
+ * morphNormals: ,
+ *
+ * fog:
+ * }
+ */
+
+THREE.ShaderMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'ShaderMaterial';
+
+ this.defines = {};
+ this.uniforms = {};
+
+ this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}';
+ this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}';
+
+ this.shading = THREE.SmoothShading;
+
+ this.linewidth = 1;
+
+ this.wireframe = false;
+ this.wireframeLinewidth = 1;
+
+ this.fog = false; // set to use scene fog
+
+ this.lights = false; // set to use scene lights
+
+ this.vertexColors = THREE.NoColors; // set to use "color" attribute stream
+
+ this.skinning = false; // set to use skinning attribute streams
+
+ this.morphTargets = false; // set to use morph targets
+ this.morphNormals = false; // set to use morph normals
+
+ this.derivatives = false; // set to use derivatives
+
+ // When rendered geometry doesn't include these attributes but the material does,
+ // use these default values in WebGL. This avoids errors when buffer data is missing.
+ this.defaultAttributeValues = {
+ 'color': [ 1, 1, 1 ],
+ 'uv': [ 0, 0 ],
+ 'uv2': [ 0, 0 ]
+ };
+
+ this.index0AttributeName = undefined;
+
+ if ( parameters !== undefined ) {
+
+ if ( parameters.attributes !== undefined ) {
+
+ console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' );
+
+ }
+
+ this.setValues( parameters );
+
+ }
+
+};
+
+THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.ShaderMaterial.prototype.constructor = THREE.ShaderMaterial;
+
+THREE.ShaderMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.fragmentShader = source.fragmentShader;
+ this.vertexShader = source.vertexShader;
+
+ this.uniforms = THREE.UniformsUtils.clone( source.uniforms );
+
+ this.attributes = source.attributes;
+ this.defines = source.defines;
+
+ this.shading = source.shading;
+
+ this.wireframe = source.wireframe;
+ this.wireframeLinewidth = source.wireframeLinewidth;
+
+ this.fog = source.fog;
+
+ this.lights = source.lights;
+
+ this.vertexColors = source.vertexColors;
+
+ this.skinning = source.skinning;
+
+ this.morphTargets = source.morphTargets;
+ this.morphNormals = source.morphNormals;
+
+ this.derivatives = source.derivatives;
+
+ return this;
+
+};
+
+THREE.ShaderMaterial.prototype.toJSON = function ( meta ) {
+
+ var data = THREE.Material.prototype.toJSON.call( this, meta );
+
+ data.uniforms = this.uniforms;
+ data.attributes = this.attributes;
+ data.vertexShader = this.vertexShader;
+ data.fragmentShader = this.fragmentShader;
+
+ return data;
+
+};
+
+// File:src/materials/RawShaderMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.RawShaderMaterial = function ( parameters ) {
+
+ THREE.ShaderMaterial.call( this, parameters );
+
+ this.type = 'RawShaderMaterial';
+
+};
+
+THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype );
+THREE.RawShaderMaterial.prototype.constructor = THREE.RawShaderMaterial;
+// File:src/materials/SpriteMaterial.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * color: ,
+ * opacity: ,
+ * map: new THREE.Texture( ),
+ *
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * uvOffset: new THREE.Vector2(),
+ * uvScale: new THREE.Vector2(),
+ *
+ * fog:
+ * }
+ */
+
+THREE.SpriteMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'SpriteMaterial';
+
+ this.color = new THREE.Color( 0xffffff );
+ this.map = null;
+
+ this.rotation = 0;
+
+ this.fog = false;
+
+ // set parameters
+
+ this.setValues( parameters );
+
+};
+
+THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.SpriteMaterial.prototype.constructor = THREE.SpriteMaterial;
+
+THREE.SpriteMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+ this.map = source.map;
+
+ this.rotation = source.rotation;
+
+ this.fog = source.fog;
+
+ return this;
+
+};
+
+// File:src/textures/Texture.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author szimek / https://github.com/szimek/
+ */
+
+THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
+
+ Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } );
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.name = '';
+ this.sourceFile = '';
+
+ this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE;
+ this.mipmaps = [];
+
+ this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING;
+
+ this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping;
+ this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping;
+
+ this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter;
+ this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter;
+
+ this.anisotropy = anisotropy !== undefined ? anisotropy : 1;
+
+ this.format = format !== undefined ? format : THREE.RGBAFormat;
+ this.type = type !== undefined ? type : THREE.UnsignedByteType;
+
+ this.offset = new THREE.Vector2( 0, 0 );
+ this.repeat = new THREE.Vector2( 1, 1 );
+
+ this.generateMipmaps = true;
+ this.premultiplyAlpha = false;
+ this.flipY = true;
+ this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
+
+ this.version = 0;
+ this.onUpdate = null;
+
+};
+
+THREE.Texture.DEFAULT_IMAGE = undefined;
+THREE.Texture.DEFAULT_MAPPING = THREE.UVMapping;
+
+THREE.Texture.prototype = {
+
+ constructor: THREE.Texture,
+
+ set needsUpdate ( value ) {
+
+ if ( value === true ) this.version ++;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.image = source.image;
+ this.mipmaps = source.mipmaps.slice( 0 );
+
+ this.mapping = source.mapping;
+
+ this.wrapS = source.wrapS;
+ this.wrapT = source.wrapT;
+
+ this.magFilter = source.magFilter;
+ this.minFilter = source.minFilter;
+
+ this.anisotropy = source.anisotropy;
+
+ this.format = source.format;
+ this.type = source.type;
+
+ this.offset.copy( source.offset );
+ this.repeat.copy( source.repeat );
+
+ this.generateMipmaps = source.generateMipmaps;
+ this.premultiplyAlpha = source.premultiplyAlpha;
+ this.flipY = source.flipY;
+ this.unpackAlignment = source.unpackAlignment;
+
+ return this;
+
+ },
+
+ toJSON: function ( meta ) {
+
+ if ( meta.textures[ this.uuid ] !== undefined ) {
+
+ return meta.textures[ this.uuid ];
+
+ }
+
+ function getDataURL( image ) {
+
+ var canvas;
+
+ if ( image.toDataURL !== undefined ) {
+
+ canvas = image;
+
+ } else {
+
+ canvas = document.createElement( 'canvas' );
+ canvas.width = image.width;
+ canvas.height = image.height;
+
+ canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height );
+
+ }
+
+ if ( canvas.width > 2048 || canvas.height > 2048 ) {
+
+ return canvas.toDataURL( 'image/jpeg', 0.6 );
+
+ } else {
+
+ return canvas.toDataURL( 'image/png' );
+
+ }
+
+ }
+
+ var output = {
+ metadata: {
+ version: 4.4,
+ type: 'Texture',
+ generator: 'Texture.toJSON'
+ },
+
+ uuid: this.uuid,
+ name: this.name,
+
+ mapping: this.mapping,
+
+ repeat: [ this.repeat.x, this.repeat.y ],
+ offset: [ this.offset.x, this.offset.y ],
+ wrap: [ this.wrapS, this.wrapT ],
+
+ minFilter: this.minFilter,
+ magFilter: this.magFilter,
+ anisotropy: this.anisotropy
+ };
+
+ if ( this.image !== undefined ) {
+
+ // TODO: Move to THREE.Image
+
+ var image = this.image;
+
+ if ( image.uuid === undefined ) {
+
+ image.uuid = THREE.Math.generateUUID(); // UGH
+
+ }
+
+ if ( meta.images[ image.uuid ] === undefined ) {
+
+ meta.images[ image.uuid ] = {
+ uuid: image.uuid,
+ url: getDataURL( image )
+ };
+
+ }
+
+ output.image = image.uuid;
+
+ }
+
+ meta.textures[ this.uuid ] = output;
+
+ return output;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ },
+
+ transformUv: function ( uv ) {
+
+ if ( this.mapping !== THREE.UVMapping ) return;
+
+ uv.multiply( this.repeat );
+ uv.add( this.offset );
+
+ if ( uv.x < 0 || uv.x > 1 ) {
+
+ switch ( this.wrapS ) {
+
+ case THREE.RepeatWrapping:
+
+ uv.x = uv.x - Math.floor( uv.x );
+ break;
+
+ case THREE.ClampToEdgeWrapping:
+
+ uv.x = uv.x < 0 ? 0 : 1;
+ break;
+
+ case THREE.MirroredRepeatWrapping:
+
+ if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {
+
+ uv.x = Math.ceil( uv.x ) - uv.x;
+
+ } else {
+
+ uv.x = uv.x - Math.floor( uv.x );
+
+ }
+ break;
+
+ }
+
+ }
+
+ if ( uv.y < 0 || uv.y > 1 ) {
+
+ switch ( this.wrapT ) {
+
+ case THREE.RepeatWrapping:
+
+ uv.y = uv.y - Math.floor( uv.y );
+ break;
+
+ case THREE.ClampToEdgeWrapping:
+
+ uv.y = uv.y < 0 ? 0 : 1;
+ break;
+
+ case THREE.MirroredRepeatWrapping:
+
+ if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {
+
+ uv.y = Math.ceil( uv.y ) - uv.y;
+
+ } else {
+
+ uv.y = uv.y - Math.floor( uv.y );
+
+ }
+ break;
+
+ }
+
+ }
+
+ if ( this.flipY ) {
+
+ uv.y = 1 - uv.y;
+
+ }
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype );
+
+THREE.TextureIdCount = 0;
+
+// File:src/textures/CanvasTexture.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.CanvasTexture = function ( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
+
+ THREE.Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
+
+ this.needsUpdate = true;
+
+};
+
+THREE.CanvasTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.CanvasTexture.prototype.constructor = THREE.CanvasTexture;
+
+// File:src/textures/CubeTexture.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
+
+ mapping = mapping !== undefined ? mapping : THREE.CubeReflectionMapping;
+
+ THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
+
+ this.images = images;
+ this.flipY = false;
+
+};
+
+THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.CubeTexture.prototype.constructor = THREE.CubeTexture;
+
+THREE.CubeTexture.prototype.copy = function ( source ) {
+
+ THREE.Texture.prototype.copy.call( this, source );
+
+ this.images = source.images;
+
+ return this;
+
+};
+// File:src/textures/CompressedTexture.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {
+
+ THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
+
+ this.image = { width: width, height: height };
+ this.mipmaps = mipmaps;
+
+ // no flipping for cube textures
+ // (also flipping doesn't work for compressed textures )
+
+ this.flipY = false;
+
+ // can't generate mipmaps for compressed textures
+ // mips must be embedded in DDS files
+
+ this.generateMipmaps = false;
+
+};
+
+THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.CompressedTexture.prototype.constructor = THREE.CompressedTexture;
+
+// File:src/textures/DataTexture.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {
+
+ THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
+
+ this.image = { data: data, width: width, height: height };
+
+ this.magFilter = magFilter !== undefined ? magFilter : THREE.NearestFilter;
+ this.minFilter = minFilter !== undefined ? minFilter : THREE.NearestFilter;
+
+ this.flipY = false;
+ this.generateMipmaps = false;
+
+};
+
+THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.DataTexture.prototype.constructor = THREE.DataTexture;
+
+// File:src/textures/VideoTexture.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.VideoTexture = function ( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
+
+ THREE.Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
+
+ this.generateMipmaps = false;
+
+ var scope = this;
+
+ function update() {
+
+ requestAnimationFrame( update );
+
+ if ( video.readyState === video.HAVE_ENOUGH_DATA ) {
+
+ scope.needsUpdate = true;
+
+ }
+
+ }
+
+ update();
+
+};
+
+THREE.VideoTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.VideoTexture.prototype.constructor = THREE.VideoTexture;
+
+// File:src/objects/Group.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Group = function () {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Group';
+
+};
+
+THREE.Group.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Group.prototype.constructor = THREE.Group;
+// File:src/objects/Points.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Points = function ( geometry, material ) {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Points';
+
+ this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
+ this.material = material !== undefined ? material : new THREE.PointsMaterial( { color: Math.random() * 0xffffff } );
+
+};
+
+THREE.Points.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Points.prototype.constructor = THREE.Points;
+
+THREE.Points.prototype.raycast = ( function () {
+
+ var inverseMatrix = new THREE.Matrix4();
+ var ray = new THREE.Ray();
+
+ return function raycast( raycaster, intersects ) {
+
+ var object = this;
+ var geometry = object.geometry;
+ var threshold = raycaster.params.Points.threshold;
+
+ inverseMatrix.getInverse( this.matrixWorld );
+ ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
+
+ if ( geometry.boundingBox !== null ) {
+
+ if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) {
+
+ return;
+
+ }
+
+ }
+
+ var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );
+ var localThresholdSq = localThreshold * localThreshold;
+ var position = new THREE.Vector3();
+
+ function testPoint( point, index ) {
+
+ var rayPointDistanceSq = ray.distanceSqToPoint( point );
+
+ if ( rayPointDistanceSq < localThresholdSq ) {
+
+ var intersectPoint = ray.closestPointToPoint( point );
+ intersectPoint.applyMatrix4( object.matrixWorld );
+
+ var distance = raycaster.ray.origin.distanceTo( intersectPoint );
+
+ if ( distance < raycaster.near || distance > raycaster.far ) return;
+
+ intersects.push( {
+
+ distance: distance,
+ distanceToRay: Math.sqrt( rayPointDistanceSq ),
+ point: intersectPoint.clone(),
+ index: index,
+ face: null,
+ object: object
+
+ } );
+
+ }
+
+ }
+
+ if ( geometry instanceof THREE.BufferGeometry ) {
+
+ var index = geometry.index;
+ var attributes = geometry.attributes;
+ var positions = attributes.position.array;
+
+ if ( index !== null ) {
+
+ var indices = index.array;
+
+ for ( var i = 0, il = indices.length; i < il; i ++ ) {
+
+ var a = indices[ i ];
+
+ position.fromArray( positions, a * 3 );
+
+ testPoint( position, a );
+
+ }
+
+ } else {
+
+ for ( var i = 0, l = positions.length / 3; i < l; i ++ ) {
+
+ position.fromArray( positions, i * 3 );
+
+ testPoint( position, i );
+
+ }
+
+ }
+
+ } else {
+
+ var vertices = geometry.vertices;
+
+ for ( var i = 0, l = vertices.length; i < l; i ++ ) {
+
+ testPoint( vertices[ i ], i );
+
+ }
+
+ }
+
+ };
+
+}() );
+
+THREE.Points.prototype.clone = function () {
+
+ return new this.constructor( this.geometry, this.material ).copy( this );
+
+};
+
+// Backwards compatibility
+
+THREE.PointCloud = function ( geometry, material ) {
+
+ console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' );
+ return new THREE.Points( geometry, material );
+
+};
+
+THREE.ParticleSystem = function ( geometry, material ) {
+
+ console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' );
+ return new THREE.Points( geometry, material );
+
+};
+
+// File:src/objects/Line.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Line = function ( geometry, material, mode ) {
+
+ if ( mode === 1 ) {
+
+ console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' );
+ return new THREE.LineSegments( geometry, material );
+
+ }
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Line';
+
+ this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
+ this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } );
+
+};
+
+THREE.Line.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Line.prototype.constructor = THREE.Line;
+
+THREE.Line.prototype.raycast = ( function () {
+
+ var inverseMatrix = new THREE.Matrix4();
+ var ray = new THREE.Ray();
+ var sphere = new THREE.Sphere();
+
+ return function raycast( raycaster, intersects ) {
+
+ var precision = raycaster.linePrecision;
+ var precisionSq = precision * precision;
+
+ var geometry = this.geometry;
+
+ if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
+
+ // Checking boundingSphere distance to ray
+
+ sphere.copy( geometry.boundingSphere );
+ sphere.applyMatrix4( this.matrixWorld );
+
+ if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) {
+
+ return;
+
+ }
+
+ inverseMatrix.getInverse( this.matrixWorld );
+ ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
+
+ var vStart = new THREE.Vector3();
+ var vEnd = new THREE.Vector3();
+ var interSegment = new THREE.Vector3();
+ var interRay = new THREE.Vector3();
+ var step = this instanceof THREE.LineSegments ? 2 : 1;
+
+ if ( geometry instanceof THREE.BufferGeometry ) {
+
+ var index = geometry.index;
+ var attributes = geometry.attributes;
+
+ if ( index !== null ) {
+
+ var indices = index.array;
+ var positions = attributes.position.array;
+
+ for ( var i = 0, l = indices.length - 1; i < l; i += step ) {
+
+ var a = indices[ i ];
+ var b = indices[ i + 1 ];
+
+ vStart.fromArray( positions, a * 3 );
+ vEnd.fromArray( positions, b * 3 );
+
+ var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );
+
+ if ( distSq > precisionSq ) continue;
+
+ interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation
+
+ var distance = raycaster.ray.origin.distanceTo( interRay );
+
+ if ( distance < raycaster.near || distance > raycaster.far ) continue;
+
+ intersects.push( {
+
+ distance: distance,
+ // What do we want? intersection point on the ray or on the segment??
+ // point: raycaster.ray.at( distance ),
+ point: interSegment.clone().applyMatrix4( this.matrixWorld ),
+ index: i,
+ face: null,
+ faceIndex: null,
+ object: this
+
+ } );
+
+ }
+
+ } else {
+
+ var positions = attributes.position.array;
+
+ for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) {
+
+ vStart.fromArray( positions, 3 * i );
+ vEnd.fromArray( positions, 3 * i + 3 );
+
+ var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );
+
+ if ( distSq > precisionSq ) continue;
+
+ interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation
+
+ var distance = raycaster.ray.origin.distanceTo( interRay );
+
+ if ( distance < raycaster.near || distance > raycaster.far ) continue;
+
+ intersects.push( {
+
+ distance: distance,
+ // What do we want? intersection point on the ray or on the segment??
+ // point: raycaster.ray.at( distance ),
+ point: interSegment.clone().applyMatrix4( this.matrixWorld ),
+ index: i,
+ face: null,
+ faceIndex: null,
+ object: this
+
+ } );
+
+ }
+
+ }
+
+ } else if ( geometry instanceof THREE.Geometry ) {
+
+ var vertices = geometry.vertices;
+ var nbVertices = vertices.length;
+
+ for ( var i = 0; i < nbVertices - 1; i += step ) {
+
+ var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment );
+
+ if ( distSq > precisionSq ) continue;
+
+ interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation
+
+ var distance = raycaster.ray.origin.distanceTo( interRay );
+
+ if ( distance < raycaster.near || distance > raycaster.far ) continue;
+
+ intersects.push( {
+
+ distance: distance,
+ // What do we want? intersection point on the ray or on the segment??
+ // point: raycaster.ray.at( distance ),
+ point: interSegment.clone().applyMatrix4( this.matrixWorld ),
+ index: i,
+ face: null,
+ faceIndex: null,
+ object: this
+
+ } );
+
+ }
+
+ }
+
+ };
+
+}() );
+
+THREE.Line.prototype.clone = function () {
+
+ return new this.constructor( this.geometry, this.material ).copy( this );
+
+};
+
+// DEPRECATED
+
+THREE.LineStrip = 0;
+THREE.LinePieces = 1;
+
+// File:src/objects/LineSegments.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.LineSegments = function ( geometry, material ) {
+
+ THREE.Line.call( this, geometry, material );
+
+ this.type = 'LineSegments';
+
+};
+
+THREE.LineSegments.prototype = Object.create( THREE.Line.prototype );
+THREE.LineSegments.prototype.constructor = THREE.LineSegments;
+
+// File:src/objects/Mesh.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author jonobr1 / http://jonobr1.com/
+ */
+
+THREE.Mesh = function ( geometry, material ) {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Mesh';
+
+ this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
+ this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } );
+
+ this.updateMorphTargets();
+
+};
+
+THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Mesh.prototype.constructor = THREE.Mesh;
+
+THREE.Mesh.prototype.updateMorphTargets = function () {
+
+ if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) {
+
+ this.morphTargetBase = - 1;
+ this.morphTargetInfluences = [];
+ this.morphTargetDictionary = {};
+
+ for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) {
+
+ this.morphTargetInfluences.push( 0 );
+ this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m;
+
+ }
+
+ }
+
+};
+
+THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) {
+
+ if ( this.morphTargetDictionary[ name ] !== undefined ) {
+
+ return this.morphTargetDictionary[ name ];
+
+ }
+
+ console.warn( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' );
+
+ return 0;
+
+};
+
+
+THREE.Mesh.prototype.raycast = ( function () {
+
+ var inverseMatrix = new THREE.Matrix4();
+ var ray = new THREE.Ray();
+ var sphere = new THREE.Sphere();
+
+ var vA = new THREE.Vector3();
+ var vB = new THREE.Vector3();
+ var vC = new THREE.Vector3();
+
+ var tempA = new THREE.Vector3();
+ var tempB = new THREE.Vector3();
+ var tempC = new THREE.Vector3();
+
+ var uvA = new THREE.Vector2();
+ var uvB = new THREE.Vector2();
+ var uvC = new THREE.Vector2();
+
+ var barycoord = new THREE.Vector3();
+
+ var intersectionPoint = new THREE.Vector3();
+ var intersectionPointWorld = new THREE.Vector3();
+
+ function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) {
+
+ THREE.Triangle.barycoordFromPoint( point, p1, p2, p3, barycoord );
+
+ uv1.multiplyScalar( barycoord.x );
+ uv2.multiplyScalar( barycoord.y );
+ uv3.multiplyScalar( barycoord.z );
+
+ uv1.add( uv2 ).add( uv3 );
+
+ return uv1.clone();
+
+ }
+
+ function checkIntersection( object, raycaster, ray, pA, pB, pC, point ){
+
+ var intersect;
+ var material = object.material;
+
+ if ( material.side === THREE.BackSide ) {
+
+ intersect = ray.intersectTriangle( pC, pB, pA, true, point );
+
+ } else {
+
+ intersect = ray.intersectTriangle( pA, pB, pC, material.side !== THREE.DoubleSide, point );
+
+ }
+
+ if ( intersect === null ) return null;
+
+ intersectionPointWorld.copy( point );
+ intersectionPointWorld.applyMatrix4( object.matrixWorld );
+
+ var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld );
+
+ if ( distance < raycaster.near || distance > raycaster.far ) return null;
+
+ return {
+ distance: distance,
+ point: intersectionPointWorld.clone(),
+ object: object
+ };
+
+ }
+
+ function checkBufferGeometryIntersection( object, raycaster, ray, positions, uvs, a, b, c ) {
+
+ vA.fromArray( positions, a * 3 );
+ vB.fromArray( positions, b * 3 );
+ vC.fromArray( positions, c * 3 );
+
+ var intersection = checkIntersection( object, raycaster, ray, vA, vB, vC, intersectionPoint );
+
+ if ( intersection ) {
+
+ if ( uvs ) {
+
+ uvA.fromArray( uvs, a * 2 );
+ uvB.fromArray( uvs, b * 2 );
+ uvC.fromArray( uvs, c * 2 );
+
+ intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC );
+
+ }
+
+ intersection.face = new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) );
+ intersection.faceIndex = a;
+
+ }
+
+ return intersection;
+
+ }
+
+ return function raycast( raycaster, intersects ) {
+
+ var geometry = this.geometry;
+ var material = this.material;
+
+ if ( material === undefined ) return;
+
+ // Checking boundingSphere distance to ray
+
+ if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
+
+ var matrixWorld = this.matrixWorld;
+
+ sphere.copy( geometry.boundingSphere );
+ sphere.applyMatrix4( matrixWorld );
+
+ if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) return;
+
+ // Check boundingBox before continuing
+
+ inverseMatrix.getInverse( matrixWorld );
+ ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
+
+ if ( geometry.boundingBox !== null ) {
+
+ if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) return;
+
+ }
+
+ var uvs, intersection;
+
+ if ( geometry instanceof THREE.BufferGeometry ) {
+
+ var a, b, c;
+ var index = geometry.index;
+ var attributes = geometry.attributes;
+ var positions = attributes.position.array;
+
+ if ( attributes.uv !== undefined ){
+
+ uvs = attributes.uv.array;
+
+ }
+
+ if ( index !== null ) {
+
+ var indices = index.array;
+
+ for ( var i = 0, l = indices.length; i < l; i += 3 ) {
+
+ a = indices[ i ];
+ b = indices[ i + 1 ];
+ c = indices[ i + 2 ];
+
+ intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c );
+
+ if ( intersection ) {
+
+ intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics
+ intersects.push( intersection );
+
+ }
+
+ }
+
+ } else {
+
+
+ for ( var i = 0, l = positions.length; i < l; i += 9 ) {
+
+ a = i / 3;
+ b = a + 1;
+ c = a + 2;
+
+ intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c );
+
+ if ( intersection ) {
+
+ intersection.index = a; // triangle number in positions buffer semantics
+ intersects.push( intersection );
+
+ }
+
+ }
+
+ }
+
+ } else if ( geometry instanceof THREE.Geometry ) {
+
+ var fvA, fvB, fvC;
+ var isFaceMaterial = material instanceof THREE.MeshFaceMaterial;
+ var materials = isFaceMaterial === true ? material.materials : null;
+
+ var vertices = geometry.vertices;
+ var faces = geometry.faces;
+ var faceVertexUvs = geometry.faceVertexUvs[ 0 ];
+ if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs;
+
+ for ( var f = 0, fl = faces.length; f < fl; f ++ ) {
+
+ var face = faces[ f ];
+ var faceMaterial = isFaceMaterial === true ? materials[ face.materialIndex ] : material;
+
+ if ( faceMaterial === undefined ) continue;
+
+ fvA = vertices[ face.a ];
+ fvB = vertices[ face.b ];
+ fvC = vertices[ face.c ];
+
+ if ( faceMaterial.morphTargets === true ) {
+
+ var morphTargets = geometry.morphTargets;
+ var morphInfluences = this.morphTargetInfluences;
+
+ vA.set( 0, 0, 0 );
+ vB.set( 0, 0, 0 );
+ vC.set( 0, 0, 0 );
+
+ for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) {
+
+ var influence = morphInfluences[ t ];
+
+ if ( influence === 0 ) continue;
+
+ var targets = morphTargets[ t ].vertices;
+
+ vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence );
+ vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence );
+ vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence );
+
+ }
+
+ vA.add( fvA );
+ vB.add( fvB );
+ vC.add( fvC );
+
+ fvA = vA;
+ fvB = vB;
+ fvC = vC;
+
+ }
+
+ intersection = checkIntersection( this, raycaster, ray, fvA, fvB, fvC, intersectionPoint );
+
+ if ( intersection ) {
+
+ if ( uvs ) {
+
+ var uvs_f = uvs[ f ];
+ uvA.copy( uvs_f[ 0 ] );
+ uvB.copy( uvs_f[ 1 ] );
+ uvC.copy( uvs_f[ 2 ] );
+
+ intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC );
+
+ }
+
+ intersection.face = face;
+ intersection.faceIndex = f;
+ intersects.push( intersection );
+
+ }
+
+ }
+
+ }
+
+ };
+
+}() );
+
+THREE.Mesh.prototype.clone = function () {
+
+ return new this.constructor( this.geometry, this.material ).copy( this );
+
+};
+
+// File:src/objects/Bone.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author ikerr / http://verold.com
+ */
+
+THREE.Bone = function ( skin ) {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Bone';
+
+ this.skin = skin;
+
+};
+
+THREE.Bone.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Bone.prototype.constructor = THREE.Bone;
+
+THREE.Bone.prototype.copy = function ( source ) {
+
+ THREE.Object3D.prototype.copy.call( this, source );
+
+ this.skin = source.skin;
+
+ return this;
+
+};
+
+// File:src/objects/Skeleton.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author michael guerrero / http://realitymeltdown.com
+ * @author ikerr / http://verold.com
+ */
+
+THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) {
+
+ this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true;
+
+ this.identityMatrix = new THREE.Matrix4();
+
+ // copy the bone array
+
+ bones = bones || [];
+
+ this.bones = bones.slice( 0 );
+
+ // create a bone texture or an array of floats
+
+ if ( this.useVertexTexture ) {
+
+ // layout (1 matrix = 4 pixels)
+ // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
+ // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8)
+ // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16)
+ // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32)
+ // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)
+
+
+ var size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix
+ size = THREE.Math.nextPowerOfTwo( Math.ceil( size ) );
+ size = Math.max( size, 4 );
+
+ this.boneTextureWidth = size;
+ this.boneTextureHeight = size;
+
+ this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel
+ this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType );
+
+ } else {
+
+ this.boneMatrices = new Float32Array( 16 * this.bones.length );
+
+ }
+
+ // use the supplied bone inverses or calculate the inverses
+
+ if ( boneInverses === undefined ) {
+
+ this.calculateInverses();
+
+ } else {
+
+ if ( this.bones.length === boneInverses.length ) {
+
+ this.boneInverses = boneInverses.slice( 0 );
+
+ } else {
+
+ console.warn( 'THREE.Skeleton bonInverses is the wrong length.' );
+
+ this.boneInverses = [];
+
+ for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+ this.boneInverses.push( new THREE.Matrix4() );
+
+ }
+
+ }
+
+ }
+
+};
+
+THREE.Skeleton.prototype.calculateInverses = function () {
+
+ this.boneInverses = [];
+
+ for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+ var inverse = new THREE.Matrix4();
+
+ if ( this.bones[ b ] ) {
+
+ inverse.getInverse( this.bones[ b ].matrixWorld );
+
+ }
+
+ this.boneInverses.push( inverse );
+
+ }
+
+};
+
+THREE.Skeleton.prototype.pose = function () {
+
+ var bone;
+
+ // recover the bind-time world matrices
+
+ for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+ bone = this.bones[ b ];
+
+ if ( bone ) {
+
+ bone.matrixWorld.getInverse( this.boneInverses[ b ] );
+
+ }
+
+ }
+
+ // compute the local matrices, positions, rotations and scales
+
+ for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+ bone = this.bones[ b ];
+
+ if ( bone ) {
+
+ if ( bone.parent ) {
+
+ bone.matrix.getInverse( bone.parent.matrixWorld );
+ bone.matrix.multiply( bone.matrixWorld );
+
+ } else {
+
+ bone.matrix.copy( bone.matrixWorld );
+
+ }
+
+ bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
+
+ }
+
+ }
+
+};
+
+THREE.Skeleton.prototype.update = ( function () {
+
+ var offsetMatrix = new THREE.Matrix4();
+
+ return function update() {
+
+ // flatten bone matrices to array
+
+ for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+ // compute the offset between the current and the original transform
+
+ var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix;
+
+ offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] );
+ offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );
+
+ }
+
+ if ( this.useVertexTexture ) {
+
+ this.boneTexture.needsUpdate = true;
+
+ }
+
+ };
+
+} )();
+
+THREE.Skeleton.prototype.clone = function () {
+
+ return new THREE.Skeleton( this.bones, this.boneInverses, this.useVertexTexture );
+
+};
+
+// File:src/objects/SkinnedMesh.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author ikerr / http://verold.com
+ */
+
+THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
+
+ THREE.Mesh.call( this, geometry, material );
+
+ this.type = 'SkinnedMesh';
+
+ this.bindMode = "attached";
+ this.bindMatrix = new THREE.Matrix4();
+ this.bindMatrixInverse = new THREE.Matrix4();
+
+ // init bones
+
+ // TODO: remove bone creation as there is no reason (other than
+ // convenience) for THREE.SkinnedMesh to do this.
+
+ var bones = [];
+
+ if ( this.geometry && this.geometry.bones !== undefined ) {
+
+ var bone, gbone;
+
+ for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) {
+
+ gbone = this.geometry.bones[ b ];
+
+ bone = new THREE.Bone( this );
+ bones.push( bone );
+
+ bone.name = gbone.name;
+ bone.position.fromArray( gbone.pos );
+ bone.quaternion.fromArray( gbone.rotq );
+ if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl );
+
+ }
+
+ for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) {
+
+ gbone = this.geometry.bones[ b ];
+
+ if ( gbone.parent !== - 1 && gbone.parent !== null) {
+
+ bones[ gbone.parent ].add( bones[ b ] );
+
+ } else {
+
+ this.add( bones[ b ] );
+
+ }
+
+ }
+
+ }
+
+ this.normalizeSkinWeights();
+
+ this.updateMatrixWorld( true );
+ this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ), this.matrixWorld );
+
+};
+
+
+THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
+THREE.SkinnedMesh.prototype.constructor = THREE.SkinnedMesh;
+
+THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) {
+
+ this.skeleton = skeleton;
+
+ if ( bindMatrix === undefined ) {
+
+ this.updateMatrixWorld( true );
+
+ this.skeleton.calculateInverses();
+
+ bindMatrix = this.matrixWorld;
+
+ }
+
+ this.bindMatrix.copy( bindMatrix );
+ this.bindMatrixInverse.getInverse( bindMatrix );
+
+};
+
+THREE.SkinnedMesh.prototype.pose = function () {
+
+ this.skeleton.pose();
+
+};
+
+THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () {
+
+ if ( this.geometry instanceof THREE.Geometry ) {
+
+ for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) {
+
+ var sw = this.geometry.skinWeights[ i ];
+
+ var scale = 1.0 / sw.lengthManhattan();
+
+ if ( scale !== Infinity ) {
+
+ sw.multiplyScalar( scale );
+
+ } else {
+
+ sw.set( 1 ); // this will be normalized by the shader anyway
+
+ }
+
+ }
+
+ } else {
+
+ // skinning weights assumed to be normalized for THREE.BufferGeometry
+
+ }
+
+};
+
+THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) {
+
+ THREE.Mesh.prototype.updateMatrixWorld.call( this, true );
+
+ if ( this.bindMode === "attached" ) {
+
+ this.bindMatrixInverse.getInverse( this.matrixWorld );
+
+ } else if ( this.bindMode === "detached" ) {
+
+ this.bindMatrixInverse.getInverse( this.bindMatrix );
+
+ } else {
+
+ console.warn( 'THREE.SkinnedMesh unrecognized bindMode: ' + this.bindMode );
+
+ }
+
+};
+
+THREE.SkinnedMesh.prototype.clone = function() {
+
+ return new this.constructor( this.geometry, this.material, this.useVertexTexture ).copy( this );
+
+};
+
+// File:src/objects/LOD.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.LOD = function () {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'LOD';
+
+ Object.defineProperties( this, {
+ levels: {
+ enumerable: true,
+ value: []
+ },
+ objects: {
+ get: function () {
+
+ console.warn( 'THREE.LOD: .objects has been renamed to .levels.' );
+ return this.levels;
+
+ }
+ }
+ } );
+
+};
+
+
+THREE.LOD.prototype = Object.create( THREE.Object3D.prototype );
+THREE.LOD.prototype.constructor = THREE.LOD;
+
+THREE.LOD.prototype.addLevel = function ( object, distance ) {
+
+ if ( distance === undefined ) distance = 0;
+
+ distance = Math.abs( distance );
+
+ var levels = this.levels;
+
+ for ( var l = 0; l < levels.length; l ++ ) {
+
+ if ( distance < levels[ l ].distance ) {
+
+ break;
+
+ }
+
+ }
+
+ levels.splice( l, 0, { distance: distance, object: object } );
+
+ this.add( object );
+
+};
+
+THREE.LOD.prototype.getObjectForDistance = function ( distance ) {
+
+ var levels = this.levels;
+
+ for ( var i = 1, l = levels.length; i < l; i ++ ) {
+
+ if ( distance < levels[ i ].distance ) {
+
+ break;
+
+ }
+
+ }
+
+ return levels[ i - 1 ].object;
+
+};
+
+THREE.LOD.prototype.raycast = ( function () {
+
+ var matrixPosition = new THREE.Vector3();
+
+ return function raycast( raycaster, intersects ) {
+
+ matrixPosition.setFromMatrixPosition( this.matrixWorld );
+
+ var distance = raycaster.ray.origin.distanceTo( matrixPosition );
+
+ this.getObjectForDistance( distance ).raycast( raycaster, intersects );
+
+ };
+
+}() );
+
+THREE.LOD.prototype.update = function () {
+
+ var v1 = new THREE.Vector3();
+ var v2 = new THREE.Vector3();
+
+ return function update( camera ) {
+
+ var levels = this.levels;
+
+ if ( levels.length > 1 ) {
+
+ v1.setFromMatrixPosition( camera.matrixWorld );
+ v2.setFromMatrixPosition( this.matrixWorld );
+
+ var distance = v1.distanceTo( v2 );
+
+ levels[ 0 ].object.visible = true;
+
+ for ( var i = 1, l = levels.length; i < l; i ++ ) {
+
+ if ( distance >= levels[ i ].distance ) {
+
+ levels[ i - 1 ].object.visible = false;
+ levels[ i ].object.visible = true;
+
+ } else {
+
+ break;
+
+ }
+
+ }
+
+ for ( ; i < l; i ++ ) {
+
+ levels[ i ].object.visible = false;
+
+ }
+
+ }
+
+ };
+
+}();
+
+THREE.LOD.prototype.copy = function ( source ) {
+
+ THREE.Object3D.prototype.copy.call( this, source, false );
+
+ var levels = source.levels;
+
+ for ( var i = 0, l = levels.length; i < l; i ++ ) {
+
+ var level = levels[ i ];
+
+ this.addLevel( level.object.clone(), level.distance );
+
+ }
+
+ return this;
+
+};
+
+THREE.LOD.prototype.toJSON = function ( meta ) {
+
+ var data = THREE.Object3D.prototype.toJSON.call( this, meta );
+
+ data.object.levels = [];
+
+ var levels = this.levels;
+
+ for ( var i = 0, l = levels.length; i < l; i ++ ) {
+
+ var level = levels[ i ];
+
+ data.object.levels.push( {
+ object: level.object.uuid,
+ distance: level.distance
+ } );
+
+ }
+
+ return data;
+
+};
+
+// File:src/objects/Sprite.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Sprite = ( function () {
+
+ var indices = new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] );
+ var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0.5, - 0.5, 0, 0.5, 0.5, 0, - 0.5, 0.5, 0 ] );
+ var uvs = new Float32Array( [ 0, 0, 1, 0, 1, 1, 0, 1 ] );
+
+ var geometry = new THREE.BufferGeometry();
+ geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
+ geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+ geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
+
+ return function Sprite( material ) {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Sprite';
+
+ this.geometry = geometry;
+ this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial();
+
+ };
+
+} )();
+
+THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Sprite.prototype.constructor = THREE.Sprite;
+
+THREE.Sprite.prototype.raycast = ( function () {
+
+ var matrixPosition = new THREE.Vector3();
+
+ return function raycast( raycaster, intersects ) {
+
+ matrixPosition.setFromMatrixPosition( this.matrixWorld );
+
+ var distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition );
+ var guessSizeSq = this.scale.x * this.scale.y;
+
+ if ( distanceSq > guessSizeSq ) {
+
+ return;
+
+ }
+
+ intersects.push( {
+
+ distance: Math.sqrt( distanceSq ),
+ point: this.position,
+ face: null,
+ object: this
+
+ } );
+
+ };
+
+}() );
+
+THREE.Sprite.prototype.clone = function () {
+
+ return new this.constructor( this.material ).copy( this );
+
+};
+
+// Backwards compatibility
+
+THREE.Particle = THREE.Sprite;
+
+// File:src/objects/LensFlare.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.LensFlare = function ( texture, size, distance, blending, color ) {
+
+ THREE.Object3D.call( this );
+
+ this.lensFlares = [];
+
+ this.positionScreen = new THREE.Vector3();
+ this.customUpdateCallback = undefined;
+
+ if ( texture !== undefined ) {
+
+ this.add( texture, size, distance, blending, color );
+
+ }
+
+};
+
+THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype );
+THREE.LensFlare.prototype.constructor = THREE.LensFlare;
+
+
+/*
+ * Add: adds another flare
+ */
+
+THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) {
+
+ if ( size === undefined ) size = - 1;
+ if ( distance === undefined ) distance = 0;
+ if ( opacity === undefined ) opacity = 1;
+ if ( color === undefined ) color = new THREE.Color( 0xffffff );
+ if ( blending === undefined ) blending = THREE.NormalBlending;
+
+ distance = Math.min( distance, Math.max( 0, distance ) );
+
+ this.lensFlares.push( {
+ texture: texture, // THREE.Texture
+ size: size, // size in pixels (-1 = use texture.width)
+ distance: distance, // distance (0-1) from light source (0=at light source)
+ x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is in front z = 1 is back
+ scale: 1, // scale
+ rotation: 0, // rotation
+ opacity: opacity, // opacity
+ color: color, // color
+ blending: blending // blending
+ } );
+
+};
+
+/*
+ * Update lens flares update positions on all flares based on the screen position
+ * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way.
+ */
+
+THREE.LensFlare.prototype.updateLensFlares = function () {
+
+ var f, fl = this.lensFlares.length;
+ var flare;
+ var vecX = - this.positionScreen.x * 2;
+ var vecY = - this.positionScreen.y * 2;
+
+ for ( f = 0; f < fl; f ++ ) {
+
+ flare = this.lensFlares[ f ];
+
+ flare.x = this.positionScreen.x + vecX * flare.distance;
+ flare.y = this.positionScreen.y + vecY * flare.distance;
+
+ flare.wantedRotation = flare.x * Math.PI * 0.25;
+ flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;
+
+ }
+
+};
+
+THREE.LensFlare.prototype.copy = function ( source ) {
+
+ THREE.Object3D.prototype.copy.call( this, source );
+
+ this.positionScreen.copy( source.positionScreen );
+ this.customUpdateCallback = source.customUpdateCallback;
+
+ for ( var i = 0, l = source.lensFlares.length; i < l; i ++ ) {
+
+ this.lensFlares.push( source.lensFlares[ i ] );
+
+ }
+
+ return this;
+
+};
+
+// File:src/scenes/Scene.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Scene = function () {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Scene';
+
+ this.fog = null;
+ this.overrideMaterial = null;
+
+ this.autoUpdate = true; // checked by the renderer
+
+};
+
+THREE.Scene.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Scene.prototype.constructor = THREE.Scene;
+
+THREE.Scene.prototype.copy = function ( source ) {
+
+ THREE.Object3D.prototype.copy.call( this, source );
+
+ if ( source.fog !== null ) this.fog = source.fog.clone();
+ if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone();
+
+ this.autoUpdate = source.autoUpdate;
+ this.matrixAutoUpdate = source.matrixAutoUpdate;
+
+ return this;
+
+};
+
+// File:src/scenes/Fog.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Fog = function ( color, near, far ) {
+
+ this.name = '';
+
+ this.color = new THREE.Color( color );
+
+ this.near = ( near !== undefined ) ? near : 1;
+ this.far = ( far !== undefined ) ? far : 1000;
+
+};
+
+THREE.Fog.prototype.clone = function () {
+
+ return new THREE.Fog( this.color.getHex(), this.near, this.far );
+
+};
+
+// File:src/scenes/FogExp2.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.FogExp2 = function ( color, density ) {
+
+ this.name = '';
+
+ this.color = new THREE.Color( color );
+ this.density = ( density !== undefined ) ? density : 0.00025;
+
+};
+
+THREE.FogExp2.prototype.clone = function () {
+
+ return new THREE.FogExp2( this.color.getHex(), this.density );
+
+};
+
+// File:src/renderers/shaders/ShaderChunk.js
+
+THREE.ShaderChunk = {};
+
+// File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl
+
+THREE.ShaderChunk[ 'alphamap_fragment'] = "#ifdef USE_ALPHAMAP\n\n diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'alphamap_pars_fragment'] = "#ifdef USE_ALPHAMAP\n\n uniform sampler2D alphaMap;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl
+
+THREE.ShaderChunk[ 'alphatest_fragment'] = "#ifdef ALPHATEST\n\n if ( diffuseColor.a < ALPHATEST ) discard;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/aomap_fragment.glsl
+
+THREE.ShaderChunk[ 'aomap_fragment'] = "#ifdef USE_AOMAP\n\n totalAmbientLight *= ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/aomap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'aomap_pars_fragment'] = "#ifdef USE_AOMAP\n\n uniform sampler2D aoMap;\n uniform float aoMapIntensity;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/begin_vertex.glsl
+
+THREE.ShaderChunk[ 'begin_vertex'] = "\nvec3 transformed = vec3( position );\n";
+
+// File:src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl
+
+THREE.ShaderChunk[ 'beginnormal_vertex'] = "\nvec3 objectNormal = vec3( normal );\n";
+
+// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'bumpmap_pars_fragment'] = "#ifdef USE_BUMPMAP\n\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n\n\n\n vec2 dHdxy_fwd() {\n\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\n\n float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n return vec2( dBx, dBy );\n\n }\n\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm;\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n\n float fDet = dot( vSigmaX, R1 );\n\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n\n }\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl
+
+THREE.ShaderChunk[ 'color_fragment'] = "#ifdef USE_COLOR\n\n diffuseColor.rgb *= vColor;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'color_pars_fragment'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'color_pars_vertex'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl
+
+THREE.ShaderChunk[ 'color_vertex'] = "#ifdef USE_COLOR\n\n vColor.xyz = color.xyz;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/common.glsl
+
+THREE.ShaderChunk[ 'common'] = "#define PI 3.14159\n#define PI2 6.28318\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\n\nvec3 transformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz );\n\n}\n\nvec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );\n\n}\n\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n float distance = dot( planeNormal, point - pointOnPlane );\n\n return - distance * planeNormal + point;\n\n}\n\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return sign( dot( point - pointOnPlane, planeNormal ) );\n\n}\n\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n\n}\n\nfloat calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {\n\n if ( decayExponent > 0.0 ) {\n\n return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\n }\n\n return 1.0;\n\n}\n\nvec3 F_Schlick( in vec3 specularColor, in float dotLH ) {\n\n\n float fresnel = exp2( ( -5.55437 * dotLH - 6.98316 ) * dotLH );\n\n return ( 1.0 - specularColor ) * fresnel + specularColor;\n\n}\n\nfloat G_BlinnPhong_Implicit( /* in float dotNL, in float dotNV */ ) {\n\n\n return 0.25;\n\n}\n\nfloat D_BlinnPhong( in float shininess, in float dotNH ) {\n\n\n return ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n\n}\n\nvec3 BRDF_BlinnPhong( in vec3 specularColor, in float shininess, in vec3 normal, in vec3 lightDir, in vec3 viewDir ) {\n\n vec3 halfDir = normalize( lightDir + viewDir );\n\n float dotNH = saturate( dot( normal, halfDir ) );\n float dotLH = saturate( dot( lightDir, halfDir ) );\n\n vec3 F = F_Schlick( specularColor, dotLH );\n\n float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );\n\n float D = D_BlinnPhong( shininess, dotNH );\n\n return F * G * D;\n\n}\n\nvec3 inputToLinear( in vec3 a ) {\n\n #ifdef GAMMA_INPUT\n\n return pow( a, vec3( float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n\nvec3 linearToOutput( in vec3 a ) {\n\n #ifdef GAMMA_OUTPUT\n\n return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n";
+
+// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl
+
+THREE.ShaderChunk[ 'defaultnormal_vertex'] = "#ifdef FLIP_SIDED\n\n objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;\n";
+
+// File:src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl
+
+THREE.ShaderChunk[ 'displacementmap_vertex'] = "#ifdef USE_DISPLACEMENTMAP\n\n transformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'displacementmap_pars_vertex'] = "#ifdef USE_DISPLACEMENTMAP\n\n uniform sampler2D displacementMap;\n uniform float displacementScale;\n uniform float displacementBias;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl
+
+THREE.ShaderChunk[ 'emissivemap_fragment'] = "#ifdef USE_EMISSIVEMAP\n\n vec4 emissiveColor = texture2D( emissiveMap, vUv );\n\n emissiveColor.rgb = inputToLinear( emissiveColor.rgb );\n\n totalEmissiveLight *= emissiveColor.rgb;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/emissivemap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'emissivemap_pars_fragment'] = "#ifdef USE_EMISSIVEMAP\n\n uniform sampler2D emissiveMap;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl
+
+THREE.ShaderChunk[ 'envmap_fragment'] = "#ifdef USE_ENVMAP\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n #else\n\n vec3 reflectVec = vReflect;\n\n #endif\n\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n\n #ifdef ENVMAP_TYPE_CUBE\n vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n vec4 envColor = texture2D( envMap, sampleUV );\n\n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n #endif\n\n envColor.xyz = inputToLinear( envColor.xyz );\n\n #ifdef ENVMAP_BLENDING_MULTIPLY\n\n outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_MIX )\n\n outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_ADD )\n\n outgoingLight += envColor.xyz * specularStrength * reflectivity;\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'envmap_pars_fragment'] = "#ifdef USE_ENVMAP\n\n uniform float reflectivity;\n #ifdef ENVMAP_TYPE_CUBE\n uniform samplerCube envMap;\n #else\n uniform sampler2D envMap;\n #endif\n uniform float flipEnvMap;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n uniform float refractionRatio;\n\n #else\n\n varying vec3 vReflect;\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'envmap_pars_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n varying vec3 vReflect;\n\n uniform float refractionRatio;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl
+
+THREE.ShaderChunk[ 'envmap_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vReflect = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl
+
+THREE.ShaderChunk[ 'fog_fragment'] = "#ifdef USE_FOG\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n #else\n\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #endif\n\n #ifdef FOG_EXP2\n\n float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n\n #else\n\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n #endif\n \n outgoingLight = mix( outgoingLight, fogColor, fogFactor );\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'fog_pars_fragment'] = "#ifdef USE_FOG\n\n uniform vec3 fogColor;\n\n #ifdef FOG_EXP2\n\n uniform float fogDensity;\n\n #else\n\n uniform float fogNear;\n uniform float fogFar;\n #endif\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/hemilight_fragment.glsl
+
+THREE.ShaderChunk[ 'hemilight_fragment'] = "#if MAX_HEMI_LIGHTS > 0\n\n for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vec3 lightColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n totalAmbientLight += lightColor;\n\n }\n\n#endif\n\n";
+
+// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl
+
+THREE.ShaderChunk[ 'lightmap_fragment'] = "#ifdef USE_LIGHTMAP\n\n totalAmbientLight += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'lightmap_pars_fragment'] = "#ifdef USE_LIGHTMAP\n\n uniform sampler2D lightMap;\n uniform float lightMapIntensity;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'lights_lambert_pars_vertex'] = "#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl
+
+THREE.ShaderChunk[ 'lights_lambert_vertex'] = "vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vec3( 0.0 );\n\n#endif\n\nvec3 normal = normalize( transformedNormal );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lVector = pointLightPosition[ i ] - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * saturate( - dotProduct );\n\n #endif\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n #ifdef DOUBLE_SIDED\n\n float hemiDiffuseWeightBack = - 0.5 * dotProduct + 0.5;\n\n vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n #endif\n\n }\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl
+
+THREE.ShaderChunk[ 'lights_phong_fragment'] = "vec3 viewDir = normalize( vViewPosition );\n\nvec3 totalDiffuseLight = vec3( 0.0 );\nvec3 totalSpecularLight = vec3( 0.0 );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lightPosition = pointLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * cosineTerm;\n\n }\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'lights_phong_pars_fragment'] = "uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n\n varying vec3 vNormal;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'lights_phong_pars_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl
+
+THREE.ShaderChunk[ 'lights_phong_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n vWorldPosition = worldPosition.xyz;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl
+
+THREE.ShaderChunk[ 'linear_to_gamma_fragment'] = "\n outgoingLight = linearToOutput( outgoingLight );\n";
+
+// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl
+
+THREE.ShaderChunk[ 'logdepthbuf_fragment'] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'logdepthbuf_pars_fragment'] = "#ifdef USE_LOGDEPTHBUF\n\n uniform float logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'logdepthbuf_pars_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n uniform float logDepthBufFC;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl
+
+THREE.ShaderChunk[ 'logdepthbuf_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n vFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n #endif\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl
+
+THREE.ShaderChunk[ 'map_fragment'] = "#ifdef USE_MAP\n\n vec4 texelColor = texture2D( map, vUv );\n\n texelColor.xyz = inputToLinear( texelColor.xyz );\n\n diffuseColor *= texelColor;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'map_pars_fragment'] = "#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl
+
+THREE.ShaderChunk[ 'map_particle_fragment'] = "#ifdef USE_MAP\n\n diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'map_particle_pars_fragment'] = "#ifdef USE_MAP\n\n uniform vec4 offsetRepeat;\n uniform sampler2D map;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl
+
+THREE.ShaderChunk[ 'morphnormal_vertex'] = "#ifdef USE_MORPHNORMALS\n\n objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'morphtarget_pars_vertex'] = "#ifdef USE_MORPHTARGETS\n\n #ifndef USE_MORPHNORMALS\n\n uniform float morphTargetInfluences[ 8 ];\n\n #else\n\n uniform float morphTargetInfluences[ 4 ];\n\n #endif\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl
+
+THREE.ShaderChunk[ 'morphtarget_vertex'] = "#ifdef USE_MORPHTARGETS\n\n transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n #ifndef USE_MORPHNORMALS\n\n transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/normal_phong_fragment.glsl
+
+THREE.ShaderChunk[ 'normal_phong_fragment'] = "#ifndef FLAT_SHADED\n\n vec3 normal = normalize( vNormal );\n\n #ifdef DOUBLE_SIDED\n\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n #endif\n\n#else\n\n vec3 fdx = dFdx( vViewPosition );\n vec3 fdy = dFdy( vViewPosition );\n vec3 normal = normalize( cross( fdx, fdy ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n normal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n";
+
+// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'normalmap_pars_fragment'] = "#ifdef USE_NORMALMAP\n\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n\n\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n vec3 q0 = dFdx( eye_pos.xyz );\n vec3 q1 = dFdy( eye_pos.xyz );\n vec2 st0 = dFdx( vUv.st );\n vec2 st1 = dFdy( vUv.st );\n\n vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n vec3 N = normalize( surf_norm );\n\n vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n mapN.xy = normalScale * mapN.xy;\n mat3 tsn = mat3( S, T, N );\n return normalize( tsn * mapN );\n\n }\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/project_vertex.glsl
+
+THREE.ShaderChunk[ 'project_vertex'] = "#ifdef USE_SKINNING\n\n vec4 mvPosition = modelViewMatrix * skinned;\n\n#else\n\n vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;\n";
+
+// File:src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl
+
+THREE.ShaderChunk[ 'shadowmap_fragment'] = "#ifdef USE_SHADOWMAP\n\n for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n float texelSizeY = 1.0 / shadowMapSize[ i ].y;\n\n float shadow = 0.0;\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n bool isPointLight = shadowDarkness[ i ] < 0.0;\n\n if ( isPointLight ) {\n\n float realShadowDarkness = abs( shadowDarkness[ i ] );\n\n vec3 lightToPosition = vShadowCoord[ i ].xyz;\n\n #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n vec3 bd3D = normalize( lightToPosition );\n float dp = length( lightToPosition );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n\n #if defined( SHADOWMAP_TYPE_PCF )\n const float Dr = 1.25;\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n const float Dr = 2.25;\n #endif\n\n float os = Dr * 2.0 * texelSizeY;\n\n const vec3 Gsd = vec3( - 1, 0, 1 );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n shadow *= realShadowDarkness * ( 1.0 / 21.0 );\n\n #else \n vec3 bd3D = normalize( lightToPosition );\n float dp = length( lightToPosition );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n shadow *= realShadowDarkness;\n\n #endif\n\n } else {\n\n#endif \n float texelSizeX = 1.0 / shadowMapSize[ i ].x;\n\n vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n\n bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n bool inFrustum = all( inFrustumVec );\n\n bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n bool frustumTest = all( frustumTestVec );\n\n if ( frustumTest ) {\n\n #if defined( SHADOWMAP_TYPE_PCF )\n\n\n /*\n for ( float y = -1.25; y <= 1.25; y += 1.25 )\n for ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n if ( fDepth < shadowCoord.z )\n shadow += 1.0;\n }\n shadow /= 9.0;\n */\n\n shadowCoord.z += shadowBias[ i ];\n\n const float ShadowDelta = 1.0 / 9.0;\n\n float xPixelOffset = texelSizeX;\n float yPixelOffset = texelSizeY;\n\n float dx0 = - 1.25 * xPixelOffset;\n float dy0 = - 1.25 * yPixelOffset;\n float dx1 = 1.25 * xPixelOffset;\n float dy1 = 1.25 * yPixelOffset;\n\n float fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n shadow *= shadowDarkness[ i ];\n\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n\n shadowCoord.z += shadowBias[ i ];\n\n float xPixelOffset = texelSizeX;\n float yPixelOffset = texelSizeY;\n\n float dx0 = - 1.0 * xPixelOffset;\n float dy0 = - 1.0 * yPixelOffset;\n float dx1 = 1.0 * xPixelOffset;\n float dy1 = 1.0 * yPixelOffset;\n\n mat3 shadowKernel;\n mat3 depthKernel;\n\n depthKernel[ 0 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n depthKernel[ 0 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n depthKernel[ 0 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n depthKernel[ 1 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n depthKernel[ 1 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n depthKernel[ 1 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n depthKernel[ 2 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n depthKernel[ 2 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n depthKernel[ 2 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n vec3 shadowZ = vec3( shadowCoord.z );\n shadowKernel[ 0 ] = vec3( lessThan( depthKernel[ 0 ], shadowZ ) );\n shadowKernel[ 0 ] *= vec3( 0.25 );\n\n shadowKernel[ 1 ] = vec3( lessThan( depthKernel[ 1 ], shadowZ ) );\n shadowKernel[ 1 ] *= vec3( 0.25 );\n\n shadowKernel[ 2 ] = vec3( lessThan( depthKernel[ 2 ], shadowZ ) );\n shadowKernel[ 2 ] *= vec3( 0.25 );\n\n vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[ i ].xy );\n\n shadowKernel[ 0 ] = mix( shadowKernel[ 1 ], shadowKernel[ 0 ], fractionalCoord.x );\n shadowKernel[ 1 ] = mix( shadowKernel[ 2 ], shadowKernel[ 1 ], fractionalCoord.x );\n\n vec4 shadowValues;\n shadowValues.x = mix( shadowKernel[ 0 ][ 1 ], shadowKernel[ 0 ][ 0 ], fractionalCoord.y );\n shadowValues.y = mix( shadowKernel[ 0 ][ 2 ], shadowKernel[ 0 ][ 1 ], fractionalCoord.y );\n shadowValues.z = mix( shadowKernel[ 1 ][ 1 ], shadowKernel[ 1 ][ 0 ], fractionalCoord.y );\n shadowValues.w = mix( shadowKernel[ 1 ][ 2 ], shadowKernel[ 1 ][ 1 ], fractionalCoord.y );\n\n shadow = dot( shadowValues, vec4( 1.0 ) ) * shadowDarkness[ i ];\n\n #else \n shadowCoord.z += shadowBias[ i ];\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n shadow = shadowDarkness[ i ];\n\n #endif\n\n }\n\n#ifdef SHADOWMAP_DEBUG\n\n if ( inFrustum ) {\n\n if ( i == 0 ) {\n\n outgoingLight *= vec3( 1.0, 0.5, 0.0 );\n\n } else if ( i == 1 ) {\n\n outgoingLight *= vec3( 0.0, 1.0, 0.8 );\n\n } else {\n\n outgoingLight *= vec3( 0.0, 0.5, 1.0 );\n\n }\n\n }\n\n#endif\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n }\n\n#endif\n\n shadowMask = shadowMask * vec3( 1.0 - shadow );\n\n }\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'shadowmap_pars_fragment'] = "#ifdef USE_SHADOWMAP\n\n uniform sampler2D shadowMap[ MAX_SHADOWS ];\n uniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform float shadowBias[ MAX_SHADOWS ];\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n float unpackDepth( const in vec4 rgba_depth ) {\n\n const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n float depth = dot( rgba_depth, bit_shift );\n return depth;\n\n }\n\n #if defined(POINT_LIGHT_SHADOWS)\n\n\n void adjustShadowValue1K( const float testDepth, const vec4 textureData, const float bias, inout float shadowValue ) {\n\n const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n if ( testDepth >= dot( textureData, bitSh ) * 1000.0 + bias )\n shadowValue += 1.0;\n\n }\n\n\n vec2 cubeToUV( vec3 v, float texelSizeY ) {\n\n\n vec3 absV = abs( v );\n\n\n float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n absV *= scaleToCube;\n\n\n v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\n\n\n vec2 planar = v.xy;\n\n float almostATexel = 1.5 * texelSizeY;\n float almostOne = 1.0 - almostATexel;\n\n if ( absV.z >= almostOne ) {\n\n if ( v.z > 0.0 )\n planar.x = 4.0 - v.x;\n\n } else if ( absV.x >= almostOne ) {\n\n float signX = sign( v.x );\n planar.x = v.z * signX + 2.0 * signX;\n\n } else if ( absV.y >= almostOne ) {\n\n float signY = sign( v.y );\n planar.x = v.x + 2.0 * signY + 2.0;\n planar.y = v.z * signY - 2.0;\n\n }\n\n\n return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\n }\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'shadowmap_pars_vertex'] = "#ifdef USE_SHADOWMAP\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl
+
+THREE.ShaderChunk[ 'shadowmap_vertex'] = "#ifdef USE_SHADOWMAP\n\n for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n }\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl
+
+THREE.ShaderChunk[ 'skinbase_vertex'] = "#ifdef USE_SKINNING\n\n mat4 boneMatX = getBoneMatrix( skinIndex.x );\n mat4 boneMatY = getBoneMatrix( skinIndex.y );\n mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n mat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'skinning_pars_vertex'] = "#ifdef USE_SKINNING\n\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n\n #ifdef BONE_TEXTURE\n\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n\n mat4 getBoneMatrix( const in float i ) {\n\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n\n y = dy * ( y + 0.5 );\n\n vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n mat4 bone = mat4( v1, v2, v3, v4 );\n\n return bone;\n\n }\n\n #else\n\n uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n mat4 getBoneMatrix( const in float i ) {\n\n mat4 bone = boneGlobalMatrices[ int(i) ];\n return bone;\n\n }\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl
+
+THREE.ShaderChunk[ 'skinning_vertex'] = "#ifdef USE_SKINNING\n\n vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\n vec4 skinned = vec4( 0.0 );\n skinned += boneMatX * skinVertex * skinWeight.x;\n skinned += boneMatY * skinVertex * skinWeight.y;\n skinned += boneMatZ * skinVertex * skinWeight.z;\n skinned += boneMatW * skinVertex * skinWeight.w;\n skinned = bindMatrixInverse * skinned;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl
+
+THREE.ShaderChunk[ 'skinnormal_vertex'] = "#ifdef USE_SKINNING\n\n mat4 skinMatrix = mat4( 0.0 );\n skinMatrix += skinWeight.x * boneMatX;\n skinMatrix += skinWeight.y * boneMatY;\n skinMatrix += skinWeight.z * boneMatZ;\n skinMatrix += skinWeight.w * boneMatW;\n skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\n objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl
+
+THREE.ShaderChunk[ 'specularmap_fragment'] = "float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n\n#else\n\n specularStrength = 1.0;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'specularmap_pars_fragment'] = "#ifdef USE_SPECULARMAP\n\n uniform sampler2D specularMap;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/uv2_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'uv2_pars_fragment'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n varying vec2 vUv2;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/uv2_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'uv2_pars_vertex'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n attribute vec2 uv2;\n varying vec2 vUv2;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/uv2_vertex.glsl
+
+THREE.ShaderChunk[ 'uv2_vertex'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n vUv2 = uv2;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'uv_pars_fragment'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'uv_pars_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/uv_vertex.glsl
+
+THREE.ShaderChunk[ 'uv_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl
+
+THREE.ShaderChunk[ 'worldpos_vertex'] = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n #ifdef USE_SKINNING\n\n vec4 worldPosition = modelMatrix * skinned;\n\n #else\n\n vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/UniformsUtils.js
+
+/**
+ * Uniform Utilities
+ */
+
+THREE.UniformsUtils = {
+
+ merge: function ( uniforms ) {
+
+ var merged = {};
+
+ for ( var u = 0; u < uniforms.length; u ++ ) {
+
+ var tmp = this.clone( uniforms[ u ] );
+
+ for ( var p in tmp ) {
+
+ merged[ p ] = tmp[ p ];
+
+ }
+
+ }
+
+ return merged;
+
+ },
+
+ clone: function ( uniforms_src ) {
+
+ var uniforms_dst = {};
+
+ for ( var u in uniforms_src ) {
+
+ uniforms_dst[ u ] = {};
+
+ for ( var p in uniforms_src[ u ] ) {
+
+ var parameter_src = uniforms_src[ u ][ p ];
+
+ if ( parameter_src instanceof THREE.Color ||
+ parameter_src instanceof THREE.Vector2 ||
+ parameter_src instanceof THREE.Vector3 ||
+ parameter_src instanceof THREE.Vector4 ||
+ parameter_src instanceof THREE.Matrix3 ||
+ parameter_src instanceof THREE.Matrix4 ||
+ parameter_src instanceof THREE.Texture ) {
+
+ uniforms_dst[ u ][ p ] = parameter_src.clone();
+
+ } else if ( Array.isArray( parameter_src ) ) {
+
+ uniforms_dst[ u ][ p ] = parameter_src.slice();
+
+ } else {
+
+ uniforms_dst[ u ][ p ] = parameter_src;
+
+ }
+
+ }
+
+ }
+
+ return uniforms_dst;
+
+ }
+
+};
+
+// File:src/renderers/shaders/UniformsLib.js
+
+/**
+ * Uniforms library for shared webgl shaders
+ */
+
+THREE.UniformsLib = {
+
+ common: {
+
+ "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) },
+ "opacity" : { type: "f", value: 1.0 },
+
+ "map" : { type: "t", value: null },
+ "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) },
+
+ "specularMap" : { type: "t", value: null },
+ "alphaMap" : { type: "t", value: null },
+
+ "envMap" : { type: "t", value: null },
+ "flipEnvMap" : { type: "f", value: - 1 },
+ "reflectivity" : { type: "f", value: 1.0 },
+ "refractionRatio" : { type: "f", value: 0.98 }
+
+ },
+
+ aomap: {
+
+ "aoMap" : { type: "t", value: null },
+ "aoMapIntensity" : { type: "f", value: 1 },
+
+ },
+
+ lightmap: {
+
+ "lightMap" : { type: "t", value: null },
+ "lightMapIntensity" : { type: "f", value: 1 },
+
+ },
+
+ emissivemap: {
+
+ "emissiveMap" : { type: "t", value: null },
+
+ },
+
+ bumpmap: {
+
+ "bumpMap" : { type: "t", value: null },
+ "bumpScale" : { type: "f", value: 1 }
+
+ },
+
+ normalmap: {
+
+ "normalMap" : { type: "t", value: null },
+ "normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) }
+
+ },
+
+ displacementmap: {
+
+ "displacementMap" : { type: "t", value: null },
+ "displacementScale" : { type: "f", value: 1 },
+ "displacementBias" : { type: "f", value: 0 }
+
+ },
+
+ fog : {
+
+ "fogDensity" : { type: "f", value: 0.00025 },
+ "fogNear" : { type: "f", value: 1 },
+ "fogFar" : { type: "f", value: 2000 },
+ "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }
+
+ },
+
+ lights: {
+
+ "ambientLightColor" : { type: "fv", value: [] },
+
+ "directionalLightDirection" : { type: "fv", value: [] },
+ "directionalLightColor" : { type: "fv", value: [] },
+
+ "hemisphereLightDirection" : { type: "fv", value: [] },
+ "hemisphereLightSkyColor" : { type: "fv", value: [] },
+ "hemisphereLightGroundColor" : { type: "fv", value: [] },
+
+ "pointLightColor" : { type: "fv", value: [] },
+ "pointLightPosition" : { type: "fv", value: [] },
+ "pointLightDistance" : { type: "fv1", value: [] },
+ "pointLightDecay" : { type: "fv1", value: [] },
+
+ "spotLightColor" : { type: "fv", value: [] },
+ "spotLightPosition" : { type: "fv", value: [] },
+ "spotLightDirection" : { type: "fv", value: [] },
+ "spotLightDistance" : { type: "fv1", value: [] },
+ "spotLightAngleCos" : { type: "fv1", value: [] },
+ "spotLightExponent" : { type: "fv1", value: [] },
+ "spotLightDecay" : { type: "fv1", value: [] }
+
+ },
+
+ points: {
+
+ "psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) },
+ "opacity" : { type: "f", value: 1.0 },
+ "size" : { type: "f", value: 1.0 },
+ "scale" : { type: "f", value: 1.0 },
+ "map" : { type: "t", value: null },
+ "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) },
+
+ "fogDensity" : { type: "f", value: 0.00025 },
+ "fogNear" : { type: "f", value: 1 },
+ "fogFar" : { type: "f", value: 2000 },
+ "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }
+
+ },
+
+ shadowmap: {
+
+ "shadowMap": { type: "tv", value: [] },
+ "shadowMapSize": { type: "v2v", value: [] },
+
+ "shadowBias" : { type: "fv1", value: [] },
+ "shadowDarkness": { type: "fv1", value: [] },
+
+ "shadowMatrix" : { type: "m4v", value: [] }
+
+ }
+
+};
+
+// File:src/renderers/shaders/ShaderLib.js
+
+/**
+ * Webgl Shader Library for three.js
+ *
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author mikael emtinger / http://gomo.se/
+ */
+
+
+THREE.ShaderLib = {
+
+ 'basic': {
+
+ uniforms: THREE.UniformsUtils.merge( [
+
+ THREE.UniformsLib[ "common" ],
+ THREE.UniformsLib[ "aomap" ],
+ THREE.UniformsLib[ "fog" ],
+ THREE.UniformsLib[ "shadowmap" ]
+
+ ] ),
+
+ vertexShader: [
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "uv_pars_vertex" ],
+ THREE.ShaderChunk[ "uv2_pars_vertex" ],
+ THREE.ShaderChunk[ "envmap_pars_vertex" ],
+ THREE.ShaderChunk[ "color_pars_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+ THREE.ShaderChunk[ "skinning_pars_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "uv_vertex" ],
+ THREE.ShaderChunk[ "uv2_vertex" ],
+ THREE.ShaderChunk[ "color_vertex" ],
+ THREE.ShaderChunk[ "skinbase_vertex" ],
+
+ " #ifdef USE_ENVMAP",
+
+ THREE.ShaderChunk[ "beginnormal_vertex" ],
+ THREE.ShaderChunk[ "morphnormal_vertex" ],
+ THREE.ShaderChunk[ "skinnormal_vertex" ],
+ THREE.ShaderChunk[ "defaultnormal_vertex" ],
+
+ " #endif",
+
+ THREE.ShaderChunk[ "begin_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_vertex" ],
+ THREE.ShaderChunk[ "skinning_vertex" ],
+ THREE.ShaderChunk[ "project_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ THREE.ShaderChunk[ "worldpos_vertex" ],
+ THREE.ShaderChunk[ "envmap_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform vec3 diffuse;",
+ "uniform float opacity;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "color_pars_fragment" ],
+ THREE.ShaderChunk[ "uv_pars_fragment" ],
+ THREE.ShaderChunk[ "uv2_pars_fragment" ],
+ THREE.ShaderChunk[ "map_pars_fragment" ],
+ THREE.ShaderChunk[ "alphamap_pars_fragment" ],
+ THREE.ShaderChunk[ "aomap_pars_fragment" ],
+ THREE.ShaderChunk[ "envmap_pars_fragment" ],
+ THREE.ShaderChunk[ "fog_pars_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+ THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ " vec3 outgoingLight = vec3( 0.0 );",
+ " vec4 diffuseColor = vec4( diffuse, opacity );",
+ " vec3 totalAmbientLight = vec3( 1.0 );", // hardwired
+ " vec3 shadowMask = vec3( 1.0 );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+ THREE.ShaderChunk[ "map_fragment" ],
+ THREE.ShaderChunk[ "color_fragment" ],
+ THREE.ShaderChunk[ "alphamap_fragment" ],
+ THREE.ShaderChunk[ "alphatest_fragment" ],
+ THREE.ShaderChunk[ "specularmap_fragment" ],
+ THREE.ShaderChunk[ "aomap_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_fragment" ],
+
+ " outgoingLight = diffuseColor.rgb * totalAmbientLight * shadowMask;",
+
+ THREE.ShaderChunk[ "envmap_fragment" ],
+
+ THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
+
+ THREE.ShaderChunk[ "fog_fragment" ],
+
+ " gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ 'lambert': {
+
+ uniforms: THREE.UniformsUtils.merge( [
+
+ THREE.UniformsLib[ "common" ],
+ THREE.UniformsLib[ "fog" ],
+ THREE.UniformsLib[ "lights" ],
+ THREE.UniformsLib[ "shadowmap" ],
+
+ {
+ "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }
+ }
+
+ ] ),
+
+ vertexShader: [
+
+ "#define LAMBERT",
+
+ "varying vec3 vLightFront;",
+
+ "#ifdef DOUBLE_SIDED",
+
+ " varying vec3 vLightBack;",
+
+ "#endif",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "uv_pars_vertex" ],
+ THREE.ShaderChunk[ "uv2_pars_vertex" ],
+ THREE.ShaderChunk[ "envmap_pars_vertex" ],
+ THREE.ShaderChunk[ "lights_lambert_pars_vertex" ],
+ THREE.ShaderChunk[ "color_pars_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+ THREE.ShaderChunk[ "skinning_pars_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "uv_vertex" ],
+ THREE.ShaderChunk[ "uv2_vertex" ],
+ THREE.ShaderChunk[ "color_vertex" ],
+
+ THREE.ShaderChunk[ "beginnormal_vertex" ],
+ THREE.ShaderChunk[ "morphnormal_vertex" ],
+ THREE.ShaderChunk[ "skinbase_vertex" ],
+ THREE.ShaderChunk[ "skinnormal_vertex" ],
+ THREE.ShaderChunk[ "defaultnormal_vertex" ],
+
+ THREE.ShaderChunk[ "begin_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_vertex" ],
+ THREE.ShaderChunk[ "skinning_vertex" ],
+ THREE.ShaderChunk[ "project_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ THREE.ShaderChunk[ "worldpos_vertex" ],
+ THREE.ShaderChunk[ "envmap_vertex" ],
+ THREE.ShaderChunk[ "lights_lambert_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform vec3 diffuse;",
+ "uniform vec3 emissive;",
+ "uniform float opacity;",
+
+ "uniform vec3 ambientLightColor;",
+
+ "varying vec3 vLightFront;",
+
+ "#ifdef DOUBLE_SIDED",
+
+ " varying vec3 vLightBack;",
+
+ "#endif",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "color_pars_fragment" ],
+ THREE.ShaderChunk[ "uv_pars_fragment" ],
+ THREE.ShaderChunk[ "uv2_pars_fragment" ],
+ THREE.ShaderChunk[ "map_pars_fragment" ],
+ THREE.ShaderChunk[ "alphamap_pars_fragment" ],
+ THREE.ShaderChunk[ "envmap_pars_fragment" ],
+ THREE.ShaderChunk[ "fog_pars_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+ THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ " vec3 outgoingLight = vec3( 0.0 );", // outgoing light does not have an alpha, the surface does
+ " vec4 diffuseColor = vec4( diffuse, opacity );",
+ " vec3 totalAmbientLight = ambientLightColor;",
+ " vec3 shadowMask = vec3( 1.0 );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+ THREE.ShaderChunk[ "map_fragment" ],
+ THREE.ShaderChunk[ "color_fragment" ],
+ THREE.ShaderChunk[ "alphamap_fragment" ],
+ THREE.ShaderChunk[ "alphatest_fragment" ],
+ THREE.ShaderChunk[ "specularmap_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_fragment" ],
+
+ " #ifdef DOUBLE_SIDED",
+
+ " if ( gl_FrontFacing )",
+ " outgoingLight += diffuseColor.rgb * ( vLightFront * shadowMask + totalAmbientLight ) + emissive;",
+ " else",
+ " outgoingLight += diffuseColor.rgb * ( vLightBack * shadowMask + totalAmbientLight ) + emissive;",
+
+ " #else",
+
+ " outgoingLight += diffuseColor.rgb * ( vLightFront * shadowMask + totalAmbientLight ) + emissive;",
+
+ " #endif",
+
+ THREE.ShaderChunk[ "envmap_fragment" ],
+
+ THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
+
+ THREE.ShaderChunk[ "fog_fragment" ],
+
+ " gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ 'phong': {
+
+ uniforms: THREE.UniformsUtils.merge( [
+
+ THREE.UniformsLib[ "common" ],
+ THREE.UniformsLib[ "aomap" ],
+ THREE.UniformsLib[ "lightmap" ],
+ THREE.UniformsLib[ "emissivemap" ],
+ THREE.UniformsLib[ "bumpmap" ],
+ THREE.UniformsLib[ "normalmap" ],
+ THREE.UniformsLib[ "displacementmap" ],
+ THREE.UniformsLib[ "fog" ],
+ THREE.UniformsLib[ "lights" ],
+ THREE.UniformsLib[ "shadowmap" ],
+
+ {
+ "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) },
+ "specular" : { type: "c", value: new THREE.Color( 0x111111 ) },
+ "shininess": { type: "f", value: 30 }
+ }
+
+ ] ),
+
+ vertexShader: [
+
+ "#define PHONG",
+
+ "varying vec3 vViewPosition;",
+
+ "#ifndef FLAT_SHADED",
+
+ " varying vec3 vNormal;",
+
+ "#endif",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "uv_pars_vertex" ],
+ THREE.ShaderChunk[ "uv2_pars_vertex" ],
+ THREE.ShaderChunk[ "displacementmap_pars_vertex" ],
+ THREE.ShaderChunk[ "envmap_pars_vertex" ],
+ THREE.ShaderChunk[ "lights_phong_pars_vertex" ],
+ THREE.ShaderChunk[ "color_pars_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+ THREE.ShaderChunk[ "skinning_pars_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "uv_vertex" ],
+ THREE.ShaderChunk[ "uv2_vertex" ],
+ THREE.ShaderChunk[ "color_vertex" ],
+
+ THREE.ShaderChunk[ "beginnormal_vertex" ],
+ THREE.ShaderChunk[ "morphnormal_vertex" ],
+ THREE.ShaderChunk[ "skinbase_vertex" ],
+ THREE.ShaderChunk[ "skinnormal_vertex" ],
+ THREE.ShaderChunk[ "defaultnormal_vertex" ],
+
+ "#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED
+
+ " vNormal = normalize( transformedNormal );",
+
+ "#endif",
+
+ THREE.ShaderChunk[ "begin_vertex" ],
+ THREE.ShaderChunk[ "displacementmap_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_vertex" ],
+ THREE.ShaderChunk[ "skinning_vertex" ],
+ THREE.ShaderChunk[ "project_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ " vViewPosition = - mvPosition.xyz;",
+
+ THREE.ShaderChunk[ "worldpos_vertex" ],
+ THREE.ShaderChunk[ "envmap_vertex" ],
+ THREE.ShaderChunk[ "lights_phong_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "#define PHONG",
+
+ "uniform vec3 diffuse;",
+ "uniform vec3 emissive;",
+ "uniform vec3 specular;",
+ "uniform float shininess;",
+ "uniform float opacity;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "color_pars_fragment" ],
+ THREE.ShaderChunk[ "uv_pars_fragment" ],
+ THREE.ShaderChunk[ "uv2_pars_fragment" ],
+ THREE.ShaderChunk[ "map_pars_fragment" ],
+ THREE.ShaderChunk[ "alphamap_pars_fragment" ],
+ THREE.ShaderChunk[ "aomap_pars_fragment" ],
+ THREE.ShaderChunk[ "lightmap_pars_fragment" ],
+ THREE.ShaderChunk[ "emissivemap_pars_fragment" ],
+ THREE.ShaderChunk[ "envmap_pars_fragment" ],
+ THREE.ShaderChunk[ "fog_pars_fragment" ],
+ THREE.ShaderChunk[ "lights_phong_pars_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+ THREE.ShaderChunk[ "bumpmap_pars_fragment" ],
+ THREE.ShaderChunk[ "normalmap_pars_fragment" ],
+ THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ " vec3 outgoingLight = vec3( 0.0 );",
+ " vec4 diffuseColor = vec4( diffuse, opacity );",
+ " vec3 totalAmbientLight = ambientLightColor;",
+ " vec3 totalEmissiveLight = emissive;",
+ " vec3 shadowMask = vec3( 1.0 );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+ THREE.ShaderChunk[ "map_fragment" ],
+ THREE.ShaderChunk[ "color_fragment" ],
+ THREE.ShaderChunk[ "alphamap_fragment" ],
+ THREE.ShaderChunk[ "alphatest_fragment" ],
+ THREE.ShaderChunk[ "specularmap_fragment" ],
+ THREE.ShaderChunk[ "normal_phong_fragment" ],
+ THREE.ShaderChunk[ "lightmap_fragment" ],
+ THREE.ShaderChunk[ "hemilight_fragment" ],
+ THREE.ShaderChunk[ "aomap_fragment" ],
+ THREE.ShaderChunk[ "emissivemap_fragment" ],
+
+ THREE.ShaderChunk[ "lights_phong_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_fragment" ],
+
+ "totalDiffuseLight *= shadowMask;",
+ "totalSpecularLight *= shadowMask;",
+
+ "#ifdef METAL",
+
+ " outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) * specular + totalSpecularLight + totalEmissiveLight;",
+
+ "#else",
+
+ " outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) + totalSpecularLight + totalEmissiveLight;",
+
+ "#endif",
+
+ THREE.ShaderChunk[ "envmap_fragment" ],
+
+ THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
+
+ THREE.ShaderChunk[ "fog_fragment" ],
+
+ " gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ 'points': {
+
+ uniforms: THREE.UniformsUtils.merge( [
+
+ THREE.UniformsLib[ "points" ],
+ THREE.UniformsLib[ "shadowmap" ]
+
+ ] ),
+
+ vertexShader: [
+
+ "uniform float size;",
+ "uniform float scale;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "color_pars_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "color_vertex" ],
+
+ " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+
+ " #ifdef USE_SIZEATTENUATION",
+ " gl_PointSize = size * ( scale / length( mvPosition.xyz ) );",
+ " #else",
+ " gl_PointSize = size;",
+ " #endif",
+
+ " gl_Position = projectionMatrix * mvPosition;",
+
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+ THREE.ShaderChunk[ "worldpos_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform vec3 psColor;",
+ "uniform float opacity;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "color_pars_fragment" ],
+ THREE.ShaderChunk[ "map_particle_pars_fragment" ],
+ THREE.ShaderChunk[ "fog_pars_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ " vec3 outgoingLight = vec3( 0.0 );",
+ " vec4 diffuseColor = vec4( psColor, opacity );",
+ " vec3 shadowMask = vec3( 1.0 );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+ THREE.ShaderChunk[ "map_particle_fragment" ],
+ THREE.ShaderChunk[ "color_fragment" ],
+ THREE.ShaderChunk[ "alphatest_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_fragment" ],
+
+ " outgoingLight = diffuseColor.rgb * shadowMask;",
+
+ THREE.ShaderChunk[ "fog_fragment" ],
+
+ " gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ 'dashed': {
+
+ uniforms: THREE.UniformsUtils.merge( [
+
+ THREE.UniformsLib[ "common" ],
+ THREE.UniformsLib[ "fog" ],
+
+ {
+ "scale" : { type: "f", value: 1 },
+ "dashSize" : { type: "f", value: 1 },
+ "totalSize": { type: "f", value: 2 }
+ }
+
+ ] ),
+
+ vertexShader: [
+
+ "uniform float scale;",
+ "attribute float lineDistance;",
+
+ "varying float vLineDistance;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "color_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "color_vertex" ],
+
+ " vLineDistance = scale * lineDistance;",
+
+ " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+ " gl_Position = projectionMatrix * mvPosition;",
+
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform vec3 diffuse;",
+ "uniform float opacity;",
+
+ "uniform float dashSize;",
+ "uniform float totalSize;",
+
+ "varying float vLineDistance;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "color_pars_fragment" ],
+ THREE.ShaderChunk[ "fog_pars_fragment" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ " if ( mod( vLineDistance, totalSize ) > dashSize ) {",
+
+ " discard;",
+
+ " }",
+
+ " vec3 outgoingLight = vec3( 0.0 );",
+ " vec4 diffuseColor = vec4( diffuse, opacity );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+ THREE.ShaderChunk[ "color_fragment" ],
+
+ " outgoingLight = diffuseColor.rgb;", // simple shader
+
+ THREE.ShaderChunk[ "fog_fragment" ],
+
+ " gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ 'depth': {
+
+ uniforms: {
+
+ "mNear": { type: "f", value: 1.0 },
+ "mFar" : { type: "f", value: 2000.0 },
+ "opacity" : { type: "f", value: 1.0 }
+
+ },
+
+ vertexShader: [
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "begin_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_vertex" ],
+ THREE.ShaderChunk[ "project_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform float mNear;",
+ "uniform float mFar;",
+ "uniform float opacity;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+
+ " #ifdef USE_LOGDEPTHBUF_EXT",
+
+ " float depth = gl_FragDepthEXT / gl_FragCoord.w;",
+
+ " #else",
+
+ " float depth = gl_FragCoord.z / gl_FragCoord.w;",
+
+ " #endif",
+
+ " float color = 1.0 - smoothstep( mNear, mFar, depth );",
+ " gl_FragColor = vec4( vec3( color ), opacity );",
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ 'normal': {
+
+ uniforms: {
+
+ "opacity" : { type: "f", value: 1.0 }
+
+ },
+
+ vertexShader: [
+
+ "varying vec3 vNormal;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ " vNormal = normalize( normalMatrix * normal );",
+
+ THREE.ShaderChunk[ "begin_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_vertex" ],
+ THREE.ShaderChunk[ "project_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform float opacity;",
+ "varying vec3 vNormal;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ " gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ /* -------------------------------------------------------------------------
+ // Cube map shader
+ ------------------------------------------------------------------------- */
+
+ 'cube': {
+
+ uniforms: { "tCube": { type: "t", value: null },
+ "tFlip": { type: "f", value: - 1 } },
+
+ vertexShader: [
+
+ "varying vec3 vWorldPosition;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ " vWorldPosition = transformDirection( position, modelMatrix );",
+
+ " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform samplerCube tCube;",
+ "uniform float tFlip;",
+
+ "varying vec3 vWorldPosition;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ /* -------------------------------------------------------------------------
+ // Cube map shader
+ ------------------------------------------------------------------------- */
+
+ 'equirect': {
+
+ uniforms: { "tEquirect": { type: "t", value: null },
+ "tFlip": { type: "f", value: - 1 } },
+
+ vertexShader: [
+
+ "varying vec3 vWorldPosition;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ " vWorldPosition = transformDirection( position, modelMatrix );",
+
+ " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform sampler2D tEquirect;",
+ "uniform float tFlip;",
+
+ "varying vec3 vWorldPosition;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ // " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",
+ "vec3 direction = normalize( vWorldPosition );",
+ "vec2 sampleUV;",
+ "sampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );",
+ "sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;",
+ "gl_FragColor = texture2D( tEquirect, sampleUV );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ /* Depth encoding into RGBA texture
+ *
+ * based on SpiderGL shadow map example
+ * http://spidergl.org/example.php?id=6
+ *
+ * originally from
+ * http://www.gamedev.net/topic/442138-packing-a-float-into-a-a8r8g8b8-texture-shader/page__whichpage__1%25EF%25BF%25BD
+ *
+ * see also
+ * http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/
+ */
+
+ 'depthRGBA': {
+
+ uniforms: {},
+
+ vertexShader: [
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+ THREE.ShaderChunk[ "skinning_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "skinbase_vertex" ],
+
+ THREE.ShaderChunk[ "begin_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_vertex" ],
+ THREE.ShaderChunk[ "skinning_vertex" ],
+ THREE.ShaderChunk[ "project_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "vec4 pack_depth( const in float depth ) {",
+
+ " const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );",
+ " const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );",
+ " vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", // " vec4 res = fract( depth * bit_shift );",
+ " res -= res.xxyz * bit_mask;",
+ " return res;",
+
+ "}",
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+
+ " #ifdef USE_LOGDEPTHBUF_EXT",
+
+ " gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );",
+
+ " #else",
+
+ " gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );",
+
+ " #endif",
+
+ //"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z / gl_FragCoord.w );",
+ //"float z = ( ( gl_FragCoord.z / gl_FragCoord.w ) - 3.0 ) / ( 4000.0 - 3.0 );",
+ //"gl_FragData[ 0 ] = pack_depth( z );",
+ //"gl_FragData[ 0 ] = vec4( z, z, z, 1.0 );",
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+
+ 'distanceRGBA': {
+
+ uniforms: {
+
+ "lightPos": { type: "v3", value: new THREE.Vector3( 0, 0, 0 ) }
+
+ },
+
+ vertexShader: [
+
+ "varying vec4 vWorldPosition;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+ THREE.ShaderChunk[ "skinning_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "skinbase_vertex" ],
+ THREE.ShaderChunk[ "begin_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_vertex" ],
+ THREE.ShaderChunk[ "skinning_vertex" ],
+ THREE.ShaderChunk[ "project_vertex" ],
+ THREE.ShaderChunk[ "worldpos_vertex" ],
+
+ "vWorldPosition = worldPosition;",
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform vec3 lightPos;",
+ "varying vec4 vWorldPosition;",
+
+ THREE.ShaderChunk[ "common" ],
+
+ "vec4 pack1K ( float depth ) {",
+
+ " depth /= 1000.0;",
+ " const vec4 bitSh = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );",
+ " const vec4 bitMsk = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );",
+ " vec4 res = fract( depth * bitSh );",
+ " res -= res.xxyz * bitMsk;",
+ " return res; ",
+
+ "}",
+
+ "float unpack1K ( vec4 color ) {",
+
+ " const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );",
+ " return dot( color, bitSh ) * 1000.0;",
+
+ "}",
+
+ "void main () {",
+
+ " gl_FragColor = pack1K( length( vWorldPosition.xyz - lightPos.xyz ) );",
+
+ "}"
+
+ ].join( "\n" )
+
+ }
+
+};
+
+// File:src/renderers/WebGLRenderer.js
+
+/**
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author szimek / https://github.com/szimek/
+ */
+
+THREE.WebGLRenderer = function ( parameters ) {
+
+ console.log( 'THREE.WebGLRenderer', THREE.REVISION );
+
+ parameters = parameters || {};
+
+ var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ),
+ _context = parameters.context !== undefined ? parameters.context : null,
+
+ _width = _canvas.width,
+ _height = _canvas.height,
+
+ pixelRatio = 1,
+
+ _alpha = parameters.alpha !== undefined ? parameters.alpha : false,
+ _depth = parameters.depth !== undefined ? parameters.depth : true,
+ _stencil = parameters.stencil !== undefined ? parameters.stencil : true,
+ _antialias = parameters.antialias !== undefined ? parameters.antialias : false,
+ _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
+ _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
+
+ _clearColor = new THREE.Color( 0x000000 ),
+ _clearAlpha = 0;
+
+ var lights = [];
+
+ var opaqueObjects = [];
+ var opaqueObjectsLastIndex = - 1;
+ var transparentObjects = [];
+ var transparentObjectsLastIndex = - 1;
+
+ var morphInfluences = new Float32Array( 8 );
+
+
+ var sprites = [];
+ var lensFlares = [];
+
+ // public properties
+
+ this.domElement = _canvas;
+ this.context = null;
+
+ // clearing
+
+ this.autoClear = true;
+ this.autoClearColor = true;
+ this.autoClearDepth = true;
+ this.autoClearStencil = true;
+
+ // scene graph
+
+ this.sortObjects = true;
+
+ // physically based shading
+
+ this.gammaFactor = 2.0; // for backwards compatibility
+ this.gammaInput = false;
+ this.gammaOutput = false;
+
+ // morphs
+
+ this.maxMorphTargets = 8;
+ this.maxMorphNormals = 4;
+
+ // flags
+
+ this.autoScaleCubemaps = true;
+
+ // internal properties
+
+ var _this = this,
+
+ // internal state cache
+
+ _currentProgram = null,
+ _currentFramebuffer = null,
+ _currentMaterialId = - 1,
+ _currentGeometryProgram = '',
+ _currentCamera = null,
+
+ _usedTextureUnits = 0,
+
+ _viewportX = 0,
+ _viewportY = 0,
+ _viewportWidth = _canvas.width,
+ _viewportHeight = _canvas.height,
+ _currentWidth = 0,
+ _currentHeight = 0,
+
+ // frustum
+
+ _frustum = new THREE.Frustum(),
+
+ // camera matrices cache
+
+ _projScreenMatrix = new THREE.Matrix4(),
+
+ _vector3 = new THREE.Vector3(),
+
+ // light arrays cache
+
+ _direction = new THREE.Vector3(),
+
+ _lightsNeedUpdate = true,
+
+ _lights = {
+
+ ambient: [ 0, 0, 0 ],
+ directional: { length: 0, colors: [], positions: [] },
+ point: { length: 0, colors: [], positions: [], distances: [], decays: [] },
+ spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [], decays: [] },
+ hemi: { length: 0, skyColors: [], groundColors: [], positions: [] }
+
+ },
+
+ // info
+
+ _infoMemory = {
+
+ geometries: 0,
+ textures: 0
+
+ },
+
+ _infoRender = {
+
+ calls: 0,
+ vertices: 0,
+ faces: 0,
+ points: 0
+
+ };
+
+ this.info = {
+
+ render: _infoRender,
+ memory: _infoMemory,
+ programs: null
+
+ };
+
+
+ // initialize
+
+ var _gl;
+
+ try {
+
+ var attributes = {
+ alpha: _alpha,
+ depth: _depth,
+ stencil: _stencil,
+ antialias: _antialias,
+ premultipliedAlpha: _premultipliedAlpha,
+ preserveDrawingBuffer: _preserveDrawingBuffer
+ };
+
+ _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes );
+
+ if ( _gl === null ) {
+
+ if ( _canvas.getContext( 'webgl' ) !== null ) {
+
+ throw 'Error creating WebGL context with your selected attributes.';
+
+ } else {
+
+ throw 'Error creating WebGL context.';
+
+ }
+
+ }
+
+ _canvas.addEventListener( 'webglcontextlost', onContextLost, false );
+
+ } catch ( error ) {
+
+ console.error( 'THREE.WebGLRenderer: ' + error );
+
+ }
+
+ var extensions = new THREE.WebGLExtensions( _gl );
+
+ extensions.get( 'OES_texture_float' );
+ extensions.get( 'OES_texture_float_linear' );
+ extensions.get( 'OES_texture_half_float' );
+ extensions.get( 'OES_texture_half_float_linear' );
+ extensions.get( 'OES_standard_derivatives' );
+ extensions.get( 'ANGLE_instanced_arrays' );
+
+ if ( extensions.get( 'OES_element_index_uint' ) ) {
+
+ THREE.BufferGeometry.MaxIndex = 4294967296;
+
+ }
+
+ var capabilities = new THREE.WebGLCapabilities( _gl, extensions, parameters );
+
+ var state = new THREE.WebGLState( _gl, extensions, paramThreeToGL );
+ var properties = new THREE.WebGLProperties();
+ var objects = new THREE.WebGLObjects( _gl, properties, this.info );
+ var programCache = new THREE.WebGLPrograms( this, capabilities );
+
+ this.info.programs = programCache.programs;
+
+ var bufferRenderer = new THREE.WebGLBufferRenderer( _gl, extensions, _infoRender );
+ var indexedBufferRenderer = new THREE.WebGLIndexedBufferRenderer( _gl, extensions, _infoRender );
+
+ //
+
+ function glClearColor( r, g, b, a ) {
+
+ if ( _premultipliedAlpha === true ) {
+
+ r *= a; g *= a; b *= a;
+
+ }
+
+ _gl.clearColor( r, g, b, a );
+
+ }
+
+ function setDefaultGLState() {
+
+ state.init();
+
+ _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
+
+ glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+
+ }
+
+ function resetGLState() {
+
+ _currentProgram = null;
+ _currentCamera = null;
+
+ _currentGeometryProgram = '';
+ _currentMaterialId = - 1;
+
+ _lightsNeedUpdate = true;
+
+ state.reset();
+
+ }
+
+ setDefaultGLState();
+
+ this.context = _gl;
+ this.capabilities = capabilities;
+ this.extensions = extensions;
+ this.state = state;
+
+ // shadow map
+
+ var shadowMap = new THREE.WebGLShadowMap( this, lights, objects );
+
+ this.shadowMap = shadowMap;
+
+
+ // Plugins
+
+ var spritePlugin = new THREE.SpritePlugin( this, sprites );
+ var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares );
+
+ // API
+
+ this.getContext = function () {
+
+ return _gl;
+
+ };
+
+ this.getContextAttributes = function () {
+
+ return _gl.getContextAttributes();
+
+ };
+
+ this.forceContextLoss = function () {
+
+ extensions.get( 'WEBGL_lose_context' ).loseContext();
+
+ };
+
+ this.getMaxAnisotropy = ( function () {
+
+ var value;
+
+ return function getMaxAnisotropy() {
+
+ if ( value !== undefined ) return value;
+
+ var extension = extensions.get( 'EXT_texture_filter_anisotropic' );
+
+ if ( extension !== null ) {
+
+ value = _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT );
+
+ } else {
+
+ value = 0;
+
+ }
+
+ return value;
+
+ }
+
+ } )();
+
+ this.getPrecision = function () {
+
+ return capabilities.precision;
+
+ };
+
+ this.getPixelRatio = function () {
+
+ return pixelRatio;
+
+ };
+
+ this.setPixelRatio = function ( value ) {
+
+ if ( value !== undefined ) pixelRatio = value;
+
+ };
+
+ this.getSize = function () {
+
+ return {
+ width: _width,
+ height: _height
+ };
+
+ };
+
+ this.setSize = function ( width, height, updateStyle ) {
+
+ _width = width;
+ _height = height;
+
+ _canvas.width = width * pixelRatio;
+ _canvas.height = height * pixelRatio;
+
+ if ( updateStyle !== false ) {
+
+ _canvas.style.width = width + 'px';
+ _canvas.style.height = height + 'px';
+
+ }
+
+ this.setViewport( 0, 0, width, height );
+
+ };
+
+ this.setViewport = function ( x, y, width, height ) {
+
+ _viewportX = x * pixelRatio;
+ _viewportY = y * pixelRatio;
+
+ _viewportWidth = width * pixelRatio;
+ _viewportHeight = height * pixelRatio;
+
+ _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
+
+ };
+
+ this.getViewport = function ( dimensions ) {
+
+ dimensions.x = _viewportX / pixelRatio;
+ dimensions.y = _viewportY / pixelRatio;
+
+ dimensions.z = _viewportWidth / pixelRatio;
+ dimensions.w = _viewportHeight / pixelRatio;
+
+ };
+
+ this.setScissor = function ( x, y, width, height ) {
+
+ _gl.scissor(
+ x * pixelRatio,
+ y * pixelRatio,
+ width * pixelRatio,
+ height * pixelRatio
+ );
+
+ };
+
+ this.enableScissorTest = function ( boolean ) {
+
+ state.setScissorTest( boolean );
+
+ };
+
+ // Clearing
+
+ this.getClearColor = function () {
+
+ return _clearColor;
+
+ };
+
+ this.setClearColor = function ( color, alpha ) {
+
+ _clearColor.set( color );
+
+ _clearAlpha = alpha !== undefined ? alpha : 1;
+
+ glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+
+ };
+
+ this.getClearAlpha = function () {
+
+ return _clearAlpha;
+
+ };
+
+ this.setClearAlpha = function ( alpha ) {
+
+ _clearAlpha = alpha;
+
+ glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+
+ };
+
+ this.clear = function ( color, depth, stencil ) {
+
+ var bits = 0;
+
+ if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT;
+ if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT;
+ if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT;
+
+ _gl.clear( bits );
+
+ };
+
+ this.clearColor = function () {
+
+ _gl.clear( _gl.COLOR_BUFFER_BIT );
+
+ };
+
+ this.clearDepth = function () {
+
+ _gl.clear( _gl.DEPTH_BUFFER_BIT );
+
+ };
+
+ this.clearStencil = function () {
+
+ _gl.clear( _gl.STENCIL_BUFFER_BIT );
+
+ };
+
+ this.clearTarget = function ( renderTarget, color, depth, stencil ) {
+
+ this.setRenderTarget( renderTarget );
+ this.clear( color, depth, stencil );
+
+ };
+
+ // Reset
+
+ this.resetGLState = resetGLState;
+
+ this.dispose = function() {
+
+ _canvas.removeEventListener( 'webglcontextlost', onContextLost, false );
+
+ };
+
+ // Events
+
+ function onContextLost( event ) {
+
+ event.preventDefault();
+
+ resetGLState();
+ setDefaultGLState();
+
+ properties.clear();
+
+ };
+
+ function onTextureDispose( event ) {
+
+ var texture = event.target;
+
+ texture.removeEventListener( 'dispose', onTextureDispose );
+
+ deallocateTexture( texture );
+
+ _infoMemory.textures --;
+
+
+ }
+
+ function onRenderTargetDispose( event ) {
+
+ var renderTarget = event.target;
+
+ renderTarget.removeEventListener( 'dispose', onRenderTargetDispose );
+
+ deallocateRenderTarget( renderTarget );
+
+ _infoMemory.textures --;
+
+ }
+
+ function onMaterialDispose( event ) {
+
+ var material = event.target;
+
+ material.removeEventListener( 'dispose', onMaterialDispose );
+
+ deallocateMaterial( material );
+
+ }
+
+ // Buffer deallocation
+
+ function deallocateTexture( texture ) {
+
+ var textureProperties = properties.get( texture );
+
+ if ( texture.image && textureProperties.__image__webglTextureCube ) {
+
+ // cube texture
+
+ _gl.deleteTexture( textureProperties.__image__webglTextureCube );
+
+ } else {
+
+ // 2D texture
+
+ if ( textureProperties.__webglInit === undefined ) return;
+
+ _gl.deleteTexture( textureProperties.__webglTexture );
+
+ }
+
+ // remove all webgl properties
+ properties.delete( texture );
+
+ }
+
+ function deallocateRenderTarget( renderTarget ) {
+
+ var renderTargetProperties = properties.get( renderTarget );
+ var textureProperties = properties.get( renderTarget.texture );
+
+ if ( ! renderTarget || textureProperties.__webglTexture === undefined ) return;
+
+ _gl.deleteTexture( textureProperties.__webglTexture );
+
+ if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] );
+ _gl.deleteRenderbuffer( renderTargetProperties.__webglRenderbuffer[ i ] );
+
+ }
+
+ } else {
+
+ _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer );
+ _gl.deleteRenderbuffer( renderTargetProperties.__webglRenderbuffer );
+
+ }
+
+ properties.delete( renderTarget.texture );
+ properties.delete( renderTarget );
+
+ }
+
+ function deallocateMaterial( material ) {
+
+ releaseMaterialProgramReference( material );
+
+ properties.delete( material );
+
+ }
+
+
+ function releaseMaterialProgramReference( material ) {
+
+ var programInfo = properties.get( material ).program;
+
+ material.program = undefined;
+
+ if ( programInfo !== undefined ) {
+
+ programCache.releaseProgram( programInfo );
+
+ }
+
+ }
+
+ // Buffer rendering
+
+ this.renderBufferImmediate = function ( object, program, material ) {
+
+ state.initAttributes();
+
+ var buffers = properties.get( object );
+
+ if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer();
+ if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer();
+ if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer();
+ if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer();
+
+ var attributes = program.getAttributes();
+
+ if ( object.hasPositions ) {
+
+ _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position );
+ _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW );
+
+ state.enableAttribute( attributes.position );
+ _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
+
+ }
+
+ if ( object.hasNormals ) {
+
+ _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal );
+
+ if ( material.type !== 'MeshPhongMaterial' && material.shading === THREE.FlatShading ) {
+
+ for ( var i = 0, l = object.count * 3; i < l; i += 9 ) {
+
+ var array = object.normalArray;
+
+ var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3;
+ var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3;
+ var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3;
+
+ array[ i + 0 ] = nx;
+ array[ i + 1 ] = ny;
+ array[ i + 2 ] = nz;
+
+ array[ i + 3 ] = nx;
+ array[ i + 4 ] = ny;
+ array[ i + 5 ] = nz;
+
+ array[ i + 6 ] = nx;
+ array[ i + 7 ] = ny;
+ array[ i + 8 ] = nz;
+
+ }
+
+ }
+
+ _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW );
+
+ state.enableAttribute( attributes.normal );
+
+ _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 );
+
+ }
+
+ if ( object.hasUvs && material.map ) {
+
+ _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv );
+ _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW );
+
+ state.enableAttribute( attributes.uv );
+
+ _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 );
+
+ }
+
+ if ( object.hasColors && material.vertexColors !== THREE.NoColors ) {
+
+ _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color );
+ _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW );
+
+ state.enableAttribute( attributes.color );
+
+ _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 );
+
+ }
+
+ state.disableUnusedAttributes();
+
+ _gl.drawArrays( _gl.TRIANGLES, 0, object.count );
+
+ object.count = 0;
+
+ };
+
+ this.renderBufferDirect = function ( camera, lights, fog, geometry, material, object, group ) {
+
+ setMaterial( material );
+
+ var program = setProgram( camera, lights, fog, material, object );
+
+ var updateBuffers = false;
+ var geometryProgram = geometry.id + '_' + program.id + '_' + material.wireframe;
+
+ if ( geometryProgram !== _currentGeometryProgram ) {
+
+ _currentGeometryProgram = geometryProgram;
+ updateBuffers = true;
+
+ }
+
+ // morph targets
+
+ var morphTargetInfluences = object.morphTargetInfluences;
+
+ if ( morphTargetInfluences !== undefined ) {
+
+ var activeInfluences = [];
+
+ for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) {
+
+ var influence = morphTargetInfluences[ i ];
+ activeInfluences.push( [ influence, i ] );
+
+ }
+
+ activeInfluences.sort( numericalSort );
+
+ if ( activeInfluences.length > 8 ) {
+
+ activeInfluences.length = 8;
+
+ }
+
+ var morphAttributes = geometry.morphAttributes;
+
+ for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) {
+
+ var influence = activeInfluences[ i ];
+ morphInfluences[ i ] = influence[ 0 ];
+
+ if ( influence[ 0 ] !== 0 ) {
+
+ var index = influence[ 1 ];
+
+ if ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] );
+ if ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] );
+
+ } else {
+
+ if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i );
+ if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i );
+
+ }
+
+ }
+
+ var uniforms = program.getUniforms();
+
+ if ( uniforms.morphTargetInfluences !== null ) {
+
+ _gl.uniform1fv( uniforms.morphTargetInfluences, morphInfluences );
+
+ }
+
+ updateBuffers = true;
+
+ }
+
+ //
+
+ var index = geometry.index;
+ var position = geometry.attributes.position;
+
+ if ( material.wireframe === true ) {
+
+ index = objects.getWireframeAttribute( geometry );
+
+ }
+
+ var renderer;
+
+ if ( index !== null ) {
+
+ renderer = indexedBufferRenderer;
+ renderer.setIndex( index );
+
+ } else {
+
+ renderer = bufferRenderer;
+
+ }
+
+ if ( updateBuffers ) {
+
+ setupVertexAttributes( material, program, geometry );
+
+ if ( index !== null ) {
+
+ _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, objects.getAttributeBuffer( index ) );
+
+ }
+
+ }
+
+ //
+
+ var dataStart = 0;
+ var dataCount = Infinity;
+
+ if ( index !== null ) {
+
+ dataCount = index.count
+
+ } else if ( position !== undefined ) {
+
+ dataCount = position.count;
+
+ }
+
+ var rangeStart = geometry.drawRange.start;
+ var rangeCount = geometry.drawRange.count;
+
+ var groupStart = group !== null ? group.start : 0;
+ var groupCount = group !== null ? group.count : Infinity;
+
+ var drawStart = Math.max( dataStart, rangeStart, groupStart );
+ var drawEnd = Math.min( dataStart + dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1;
+
+ var drawCount = Math.max( 0, drawEnd - drawStart + 1 );
+
+ //
+
+ if ( object instanceof THREE.Mesh ) {
+
+ if ( material.wireframe === true ) {
+
+ state.setLineWidth( material.wireframeLinewidth * pixelRatio );
+ renderer.setMode( _gl.LINES );
+
+ } else {
+
+ renderer.setMode( _gl.TRIANGLES );
+
+ }
+
+ if ( geometry instanceof THREE.InstancedBufferGeometry && geometry.maxInstancedCount > 0 ) {
+
+ renderer.renderInstances( geometry );
+
+ } else {
+
+ renderer.render( drawStart, drawCount );
+
+ }
+
+ } else if ( object instanceof THREE.Line ) {
+
+ var lineWidth = material.linewidth;
+
+ if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material
+
+ state.setLineWidth( lineWidth * pixelRatio );
+
+ if ( object instanceof THREE.LineSegments ) {
+
+ renderer.setMode( _gl.LINES );
+
+ } else {
+
+ renderer.setMode( _gl.LINE_STRIP );
+
+ }
+
+ renderer.render( drawStart, drawCount );
+
+ } else if ( object instanceof THREE.Points ) {
+
+ renderer.setMode( _gl.POINTS );
+ renderer.render( drawStart, drawCount );
+
+ }
+
+ };
+
+ function setupVertexAttributes( material, program, geometry, startIndex ) {
+
+ var extension;
+
+ if ( geometry instanceof THREE.InstancedBufferGeometry ) {
+
+ extension = extensions.get( 'ANGLE_instanced_arrays' );
+
+ if ( extension === null ) {
+
+ console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
+ return;
+
+ }
+
+ }
+
+ if ( startIndex === undefined ) startIndex = 0;
+
+ state.initAttributes();
+
+ var geometryAttributes = geometry.attributes;
+
+ var programAttributes = program.getAttributes();
+
+ var materialDefaultAttributeValues = material.defaultAttributeValues;
+
+ for ( var name in programAttributes ) {
+
+ var programAttribute = programAttributes[ name ];
+
+ if ( programAttribute >= 0 ) {
+
+ var geometryAttribute = geometryAttributes[ name ];
+
+ if ( geometryAttribute !== undefined ) {
+
+ var size = geometryAttribute.itemSize;
+ var buffer = objects.getAttributeBuffer( geometryAttribute );
+
+ if ( geometryAttribute instanceof THREE.InterleavedBufferAttribute ) {
+
+ var data = geometryAttribute.data;
+ var stride = data.stride;
+ var offset = geometryAttribute.offset;
+
+ if ( data instanceof THREE.InstancedInterleavedBuffer ) {
+
+ state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute, extension );
+
+ if ( geometry.maxInstancedCount === undefined ) {
+
+ geometry.maxInstancedCount = data.meshPerAttribute * data.count;
+
+ }
+
+ } else {
+
+ state.enableAttribute( programAttribute );
+
+ }
+
+ _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );
+ _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, stride * data.array.BYTES_PER_ELEMENT, ( startIndex * stride + offset ) * data.array.BYTES_PER_ELEMENT );
+
+ } else {
+
+ if ( geometryAttribute instanceof THREE.InstancedBufferAttribute ) {
+
+ state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute, extension );
+
+ if ( geometry.maxInstancedCount === undefined ) {
+
+ geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;
+
+ }
+
+ } else {
+
+ state.enableAttribute( programAttribute );
+
+ }
+
+ _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );
+ _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32
+
+ }
+
+ } else if ( materialDefaultAttributeValues !== undefined ) {
+
+ var value = materialDefaultAttributeValues[ name ];
+
+ if ( value !== undefined ) {
+
+ switch ( value.length ) {
+
+ case 2:
+ _gl.vertexAttrib2fv( programAttribute, value );
+ break;
+
+ case 3:
+ _gl.vertexAttrib3fv( programAttribute, value );
+ break;
+
+ case 4:
+ _gl.vertexAttrib4fv( programAttribute, value );
+ break;
+
+ default:
+ _gl.vertexAttrib1fv( programAttribute, value );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ state.disableUnusedAttributes();
+
+ }
+
+ // Sorting
+
+ function numericalSort ( a, b ) {
+
+ return b[ 0 ] - a[ 0 ];
+
+ }
+
+ function painterSortStable ( a, b ) {
+
+ if ( a.object.renderOrder !== b.object.renderOrder ) {
+
+ return a.object.renderOrder - b.object.renderOrder;
+
+ } else if ( a.material.id !== b.material.id ) {
+
+ return a.material.id - b.material.id;
+
+ } else if ( a.z !== b.z ) {
+
+ return a.z - b.z;
+
+ } else {
+
+ return a.id - b.id;
+
+ }
+
+ }
+
+ function reversePainterSortStable ( a, b ) {
+
+ if ( a.object.renderOrder !== b.object.renderOrder ) {
+
+ return a.object.renderOrder - b.object.renderOrder;
+
+ } if ( a.z !== b.z ) {
+
+ return b.z - a.z;
+
+ } else {
+
+ return a.id - b.id;
+
+ }
+
+ }
+
+ // Rendering
+
+ this.render = function ( scene, camera, renderTarget, forceClear ) {
+
+ if ( camera instanceof THREE.Camera === false ) {
+
+ console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
+ return;
+
+ }
+
+ var fog = scene.fog;
+
+ // reset caching for this frame
+
+ _currentGeometryProgram = '';
+ _currentMaterialId = - 1;
+ _currentCamera = null;
+ _lightsNeedUpdate = true;
+
+ // update scene graph
+
+ if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+
+ // update camera matrices and frustum
+
+ if ( camera.parent === null ) camera.updateMatrixWorld();
+
+ camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+
+ _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+ _frustum.setFromMatrix( _projScreenMatrix );
+
+ lights.length = 0;
+
+ opaqueObjectsLastIndex = - 1;
+ transparentObjectsLastIndex = - 1;
+
+ sprites.length = 0;
+ lensFlares.length = 0;
+
+ projectObject( scene, camera );
+
+ opaqueObjects.length = opaqueObjectsLastIndex + 1;
+ transparentObjects.length = transparentObjectsLastIndex + 1;
+
+ if ( _this.sortObjects === true ) {
+
+ opaqueObjects.sort( painterSortStable );
+ transparentObjects.sort( reversePainterSortStable );
+
+ }
+
+ //
+
+ shadowMap.render( scene );
+
+ //
+
+ _infoRender.calls = 0;
+ _infoRender.vertices = 0;
+ _infoRender.faces = 0;
+ _infoRender.points = 0;
+
+ this.setRenderTarget( renderTarget );
+
+ if ( this.autoClear || forceClear ) {
+
+ this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil );
+
+ }
+
+ //
+
+ if ( scene.overrideMaterial ) {
+
+ var overrideMaterial = scene.overrideMaterial;
+
+ renderObjects( opaqueObjects, camera, lights, fog, overrideMaterial );
+ renderObjects( transparentObjects, camera, lights, fog, overrideMaterial );
+
+ } else {
+
+ // opaque pass (front-to-back order)
+
+ state.setBlending( THREE.NoBlending );
+ renderObjects( opaqueObjects, camera, lights, fog );
+
+ // transparent pass (back-to-front order)
+
+ renderObjects( transparentObjects, camera, lights, fog );
+
+ }
+
+ // custom render plugins (post pass)
+
+ spritePlugin.render( scene, camera );
+ lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight );
+
+ // Generate mipmap if we're using any kind of mipmap filtering
+
+ if ( renderTarget ) {
+
+ var texture = renderTarget.texture;
+ var isTargetPowerOfTwo = isPowerOfTwo( renderTarget );
+ if ( texture.generateMipmaps && isTargetPowerOfTwo && texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) {
+
+ updateRenderTargetMipmap( renderTarget );
+
+ }
+
+ }
+
+ // Ensure depth buffer writing is enabled so it can be cleared on next render
+
+ state.setDepthTest( true );
+ state.setDepthWrite( true );
+ state.setColorWrite( true );
+
+ // _gl.finish();
+
+ };
+
+ function pushRenderItem( object, geometry, material, z, group ) {
+
+ var array, index;
+
+ // allocate the next position in the appropriate array
+
+ if ( material.transparent ) {
+
+ array = transparentObjects;
+ index = ++ transparentObjectsLastIndex;
+
+ } else {
+
+ array = opaqueObjects;
+ index = ++ opaqueObjectsLastIndex;
+
+ }
+
+ // recycle existing render item or grow the array
+
+ var renderItem = array[ index ];
+
+ if ( renderItem !== undefined ) {
+
+ renderItem.id = object.id;
+ renderItem.object = object;
+ renderItem.geometry = geometry;
+ renderItem.material = material;
+ renderItem.z = _vector3.z;
+ renderItem.group = group;
+
+ } else {
+
+ renderItem = {
+ id: object.id,
+ object: object,
+ geometry: geometry,
+ material: material,
+ z: _vector3.z,
+ group: group
+ };
+
+ // assert( index === array.length );
+ array.push( renderItem );
+
+ }
+
+ }
+
+ function projectObject( object, camera ) {
+
+ if ( object.visible === false ) return;
+
+ if ( ( object.channels.mask & camera.channels.mask ) !== 0 ) {
+
+ if ( object instanceof THREE.Light ) {
+
+ lights.push( object );
+
+ } else if ( object instanceof THREE.Sprite ) {
+
+ sprites.push( object );
+
+ } else if ( object instanceof THREE.LensFlare ) {
+
+ lensFlares.push( object );
+
+ } else if ( object instanceof THREE.ImmediateRenderObject ) {
+
+ if ( _this.sortObjects === true ) {
+
+ _vector3.setFromMatrixPosition( object.matrixWorld );
+ _vector3.applyProjection( _projScreenMatrix );
+
+ }
+
+ pushRenderItem( object, null, object.material, _vector3.z, null );
+
+ } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) {
+
+ if ( object instanceof THREE.SkinnedMesh ) {
+
+ object.skeleton.update();
+
+ }
+
+ if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) {
+
+ var material = object.material;
+
+ if ( material.visible === true ) {
+
+ if ( _this.sortObjects === true ) {
+
+ _vector3.setFromMatrixPosition( object.matrixWorld );
+ _vector3.applyProjection( _projScreenMatrix );
+
+ }
+
+ var geometry = objects.update( object );
+
+ if ( material instanceof THREE.MeshFaceMaterial ) {
+
+ var groups = geometry.groups;
+ var materials = material.materials;
+
+ for ( var i = 0, l = groups.length; i < l; i ++ ) {
+
+ var group = groups[ i ];
+ var groupMaterial = materials[ group.materialIndex ];
+
+ if ( groupMaterial.visible === true ) {
+
+ pushRenderItem( object, geometry, groupMaterial, _vector3.z, group );
+
+ }
+
+ }
+
+ } else {
+
+ pushRenderItem( object, geometry, material, _vector3.z, null );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ var children = object.children;
+
+ for ( var i = 0, l = children.length; i < l; i ++ ) {
+
+ projectObject( children[ i ], camera );
+
+ }
+
+ }
+
+ function renderObjects( renderList, camera, lights, fog, overrideMaterial ) {
+
+ for ( var i = 0, l = renderList.length; i < l; i ++ ) {
+
+ var renderItem = renderList[ i ];
+
+ var object = renderItem.object;
+ var geometry = renderItem.geometry;
+ var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial;
+ var group = renderItem.group;
+
+ object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+ object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
+
+ if ( object instanceof THREE.ImmediateRenderObject ) {
+
+ setMaterial( material );
+
+ var program = setProgram( camera, lights, fog, material, object );
+
+ _currentGeometryProgram = '';
+
+ object.render( function ( object ) {
+
+ _this.renderBufferImmediate( object, program, material );
+
+ } );
+
+ } else {
+
+ _this.renderBufferDirect( camera, lights, fog, geometry, material, object, group );
+
+ }
+
+ }
+
+ }
+
+ function initMaterial( material, lights, fog, object ) {
+
+ var materialProperties = properties.get( material );
+
+ var parameters = programCache.getParameters( material, lights, fog, object );
+ var code = programCache.getProgramCode( material, parameters );
+
+ var program = materialProperties.program;
+ var programChange = true;
+
+ if ( program === undefined ) {
+
+ // new material
+ material.addEventListener( 'dispose', onMaterialDispose );
+
+ } else if ( program.code !== code ) {
+
+ // changed glsl or parameters
+ releaseMaterialProgramReference( material );
+
+ } else if ( parameters.shaderID !== undefined ) {
+
+ // same glsl and uniform list
+ return;
+
+ } else {
+
+ // only rebuild uniform list
+ programChange = false;
+
+ }
+
+ if ( programChange ) {
+
+ if ( parameters.shaderID ) {
+
+ var shader = THREE.ShaderLib[ parameters.shaderID ];
+
+ materialProperties.__webglShader = {
+ name: material.type,
+ uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
+ vertexShader: shader.vertexShader,
+ fragmentShader: shader.fragmentShader
+ };
+
+ } else {
+
+ materialProperties.__webglShader = {
+ name: material.type,
+ uniforms: material.uniforms,
+ vertexShader: material.vertexShader,
+ fragmentShader: material.fragmentShader
+ };
+
+ }
+
+ material.__webglShader = materialProperties.__webglShader;
+
+ program = programCache.acquireProgram( material, parameters, code );
+
+ materialProperties.program = program;
+ material.program = program;
+
+ }
+
+ var attributes = program.getAttributes();
+
+ if ( material.morphTargets ) {
+
+ material.numSupportedMorphTargets = 0;
+
+ for ( var i = 0; i < _this.maxMorphTargets; i ++ ) {
+
+ if ( attributes[ 'morphTarget' + i ] >= 0 ) {
+
+ material.numSupportedMorphTargets ++;
+
+ }
+
+ }
+
+ }
+
+ if ( material.morphNormals ) {
+
+ material.numSupportedMorphNormals = 0;
+
+ for ( i = 0; i < _this.maxMorphNormals; i ++ ) {
+
+ if ( attributes[ 'morphNormal' + i ] >= 0 ) {
+
+ material.numSupportedMorphNormals ++;
+
+ }
+
+ }
+
+ }
+
+ materialProperties.uniformsList = [];
+
+ var uniformLocations = materialProperties.program.getUniforms();
+
+ for ( var u in materialProperties.__webglShader.uniforms ) {
+
+ var location = uniformLocations[ u ];
+
+ if ( location ) {
+
+ materialProperties.uniformsList.push( [ materialProperties.__webglShader.uniforms[ u ], location ] );
+
+ }
+
+ }
+
+ }
+
+ function setMaterial( material ) {
+
+ setMaterialFaces( material );
+
+ if ( material.transparent === true ) {
+
+ state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha );
+
+ } else {
+
+ state.setBlending( THREE.NoBlending );
+
+ }
+
+ state.setDepthFunc( material.depthFunc );
+ state.setDepthTest( material.depthTest );
+ state.setDepthWrite( material.depthWrite );
+ state.setColorWrite( material.colorWrite );
+ state.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
+
+ }
+
+ function setMaterialFaces( material ) {
+
+ material.side !== THREE.DoubleSide ? state.enable( _gl.CULL_FACE ) : state.disable( _gl.CULL_FACE );
+ state.setFlipSided( material.side === THREE.BackSide );
+
+ }
+
+ function setProgram( camera, lights, fog, material, object ) {
+
+ _usedTextureUnits = 0;
+
+ var materialProperties = properties.get( material );
+
+ if ( material.needsUpdate || ! materialProperties.program ) {
+
+ initMaterial( material, lights, fog, object );
+ material.needsUpdate = false;
+
+ }
+
+ var refreshProgram = false;
+ var refreshMaterial = false;
+ var refreshLights = false;
+
+ var program = materialProperties.program,
+ p_uniforms = program.getUniforms(),
+ m_uniforms = materialProperties.__webglShader.uniforms;
+
+ if ( program.id !== _currentProgram ) {
+
+ _gl.useProgram( program.program );
+ _currentProgram = program.id;
+
+ refreshProgram = true;
+ refreshMaterial = true;
+ refreshLights = true;
+
+ }
+
+ if ( material.id !== _currentMaterialId ) {
+
+ if ( _currentMaterialId === - 1 ) refreshLights = true;
+ _currentMaterialId = material.id;
+
+ refreshMaterial = true;
+
+ }
+
+ if ( refreshProgram || camera !== _currentCamera ) {
+
+ _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
+
+ if ( capabilities.logarithmicDepthBuffer ) {
+
+ _gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
+
+ }
+
+
+ if ( camera !== _currentCamera ) _currentCamera = camera;
+
+ // load material specific uniforms
+ // (shader material also gets them for the sake of genericity)
+
+ if ( material instanceof THREE.ShaderMaterial ||
+ material instanceof THREE.MeshPhongMaterial ||
+ material.envMap ) {
+
+ if ( p_uniforms.cameraPosition !== undefined ) {
+
+ _vector3.setFromMatrixPosition( camera.matrixWorld );
+ _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z );
+
+ }
+
+ }
+
+ if ( material instanceof THREE.MeshPhongMaterial ||
+ material instanceof THREE.MeshLambertMaterial ||
+ material instanceof THREE.MeshBasicMaterial ||
+ material instanceof THREE.ShaderMaterial ||
+ material.skinning ) {
+
+ if ( p_uniforms.viewMatrix !== undefined ) {
+
+ _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements );
+
+ }
+
+ }
+
+ }
+
+ // skinning uniforms must be set even if material didn't change
+ // auto-setting of texture unit for bone texture must go before other textures
+ // not sure why, but otherwise weird things happen
+
+ if ( material.skinning ) {
+
+ if ( object.bindMatrix && p_uniforms.bindMatrix !== undefined ) {
+
+ _gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements );
+
+ }
+
+ if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== undefined ) {
+
+ _gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements );
+
+ }
+
+ if ( capabilities.floatVertexTextures && object.skeleton && object.skeleton.useVertexTexture ) {
+
+ if ( p_uniforms.boneTexture !== undefined ) {
+
+ var textureUnit = getTextureUnit();
+
+ _gl.uniform1i( p_uniforms.boneTexture, textureUnit );
+ _this.setTexture( object.skeleton.boneTexture, textureUnit );
+
+ }
+
+ if ( p_uniforms.boneTextureWidth !== undefined ) {
+
+ _gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth );
+
+ }
+
+ if ( p_uniforms.boneTextureHeight !== undefined ) {
+
+ _gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight );
+
+ }
+
+ } else if ( object.skeleton && object.skeleton.boneMatrices ) {
+
+ if ( p_uniforms.boneGlobalMatrices !== undefined ) {
+
+ _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices );
+
+ }
+
+ }
+
+ }
+
+ if ( refreshMaterial ) {
+
+ // refresh uniforms common to several materials
+
+ if ( fog && material.fog ) {
+
+ refreshUniformsFog( m_uniforms, fog );
+
+ }
+
+ if ( material instanceof THREE.MeshPhongMaterial ||
+ material instanceof THREE.MeshLambertMaterial ||
+ material.lights ) {
+
+ if ( _lightsNeedUpdate ) {
+
+ refreshLights = true;
+ setupLights( lights, camera );
+ _lightsNeedUpdate = false;
+
+ }
+
+ if ( refreshLights ) {
+
+ refreshUniformsLights( m_uniforms, _lights );
+ markUniformsLightsNeedsUpdate( m_uniforms, true );
+
+ } else {
+
+ markUniformsLightsNeedsUpdate( m_uniforms, false );
+
+ }
+
+ }
+
+ if ( material instanceof THREE.MeshBasicMaterial ||
+ material instanceof THREE.MeshLambertMaterial ||
+ material instanceof THREE.MeshPhongMaterial ) {
+
+ refreshUniformsCommon( m_uniforms, material );
+
+ }
+
+ // refresh single material specific uniforms
+
+ if ( material instanceof THREE.LineBasicMaterial ) {
+
+ refreshUniformsLine( m_uniforms, material );
+
+ } else if ( material instanceof THREE.LineDashedMaterial ) {
+
+ refreshUniformsLine( m_uniforms, material );
+ refreshUniformsDash( m_uniforms, material );
+
+ } else if ( material instanceof THREE.PointsMaterial ) {
+
+ refreshUniformsParticle( m_uniforms, material );
+
+ } else if ( material instanceof THREE.MeshPhongMaterial ) {
+
+ refreshUniformsPhong( m_uniforms, material );
+
+ } else if ( material instanceof THREE.MeshDepthMaterial ) {
+
+ m_uniforms.mNear.value = camera.near;
+ m_uniforms.mFar.value = camera.far;
+ m_uniforms.opacity.value = material.opacity;
+
+ } else if ( material instanceof THREE.MeshNormalMaterial ) {
+
+ m_uniforms.opacity.value = material.opacity;
+
+ }
+
+ if ( object.receiveShadow && ! material._shadowPass ) {
+
+ refreshUniformsShadow( m_uniforms, lights, camera );
+
+ }
+
+ // load common uniforms
+
+ loadUniformsGeneric( materialProperties.uniformsList );
+
+ }
+
+ loadUniformsMatrices( p_uniforms, object );
+
+ if ( p_uniforms.modelMatrix !== undefined ) {
+
+ _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements );
+
+ }
+
+ return program;
+
+ }
+
+ // Uniforms (refresh uniforms objects)
+
+ function refreshUniformsCommon ( uniforms, material ) {
+
+ uniforms.opacity.value = material.opacity;
+
+ uniforms.diffuse.value = material.color;
+
+ if ( material.emissive ) {
+
+ uniforms.emissive.value = material.emissive;
+
+ }
+
+ uniforms.map.value = material.map;
+ uniforms.specularMap.value = material.specularMap;
+ uniforms.alphaMap.value = material.alphaMap;
+
+ if ( material.aoMap ) {
+
+ uniforms.aoMap.value = material.aoMap;
+ uniforms.aoMapIntensity.value = material.aoMapIntensity;
+
+ }
+
+ // uv repeat and offset setting priorities
+ // 1. color map
+ // 2. specular map
+ // 3. normal map
+ // 4. bump map
+ // 5. alpha map
+ // 6. emissive map
+
+ var uvScaleMap;
+
+ if ( material.map ) {
+
+ uvScaleMap = material.map;
+
+ } else if ( material.specularMap ) {
+
+ uvScaleMap = material.specularMap;
+
+ } else if ( material.displacementMap ) {
+
+ uvScaleMap = material.displacementMap;
+
+ } else if ( material.normalMap ) {
+
+ uvScaleMap = material.normalMap;
+
+ } else if ( material.bumpMap ) {
+
+ uvScaleMap = material.bumpMap;
+
+ } else if ( material.alphaMap ) {
+
+ uvScaleMap = material.alphaMap;
+
+ } else if ( material.emissiveMap ) {
+
+ uvScaleMap = material.emissiveMap;
+
+ }
+
+ if ( uvScaleMap !== undefined ) {
+
+ if ( uvScaleMap instanceof THREE.WebGLRenderTarget ) uvScaleMap = uvScaleMap.texture;
+ var offset = uvScaleMap.offset;
+ var repeat = uvScaleMap.repeat;
+
+ uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );
+
+ }
+
+ uniforms.envMap.value = material.envMap;
+ uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : - 1;
+
+ uniforms.reflectivity.value = material.reflectivity;
+ uniforms.refractionRatio.value = material.refractionRatio;
+
+ }
+
+ function refreshUniformsLine ( uniforms, material ) {
+
+ uniforms.diffuse.value = material.color;
+ uniforms.opacity.value = material.opacity;
+
+ }
+
+ function refreshUniformsDash ( uniforms, material ) {
+
+ uniforms.dashSize.value = material.dashSize;
+ uniforms.totalSize.value = material.dashSize + material.gapSize;
+ uniforms.scale.value = material.scale;
+
+ }
+
+ function refreshUniformsParticle ( uniforms, material ) {
+
+ uniforms.psColor.value = material.color;
+ uniforms.opacity.value = material.opacity;
+ uniforms.size.value = material.size;
+ uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this.
+
+ uniforms.map.value = material.map;
+
+ if ( material.map !== null ) {
+
+ var offset = material.map.offset;
+ var repeat = material.map.repeat;
+
+ uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );
+
+ }
+
+ }
+
+ function refreshUniformsFog ( uniforms, fog ) {
+
+ uniforms.fogColor.value = fog.color;
+
+ if ( fog instanceof THREE.Fog ) {
+
+ uniforms.fogNear.value = fog.near;
+ uniforms.fogFar.value = fog.far;
+
+ } else if ( fog instanceof THREE.FogExp2 ) {
+
+ uniforms.fogDensity.value = fog.density;
+
+ }
+
+ }
+
+ function refreshUniformsPhong ( uniforms, material ) {
+
+ uniforms.specular.value = material.specular;
+ uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
+
+ if ( material.lightMap ) {
+
+ uniforms.lightMap.value = material.lightMap;
+ uniforms.lightMapIntensity.value = material.lightMapIntensity;
+
+ }
+
+ if ( material.emissiveMap ) {
+
+ uniforms.emissiveMap.value = material.emissiveMap;
+
+ }
+
+ if ( material.bumpMap ) {
+
+ uniforms.bumpMap.value = material.bumpMap;
+ uniforms.bumpScale.value = material.bumpScale;
+
+ }
+
+ if ( material.normalMap ) {
+
+ uniforms.normalMap.value = material.normalMap;
+ uniforms.normalScale.value.copy( material.normalScale );
+
+ }
+
+ if ( material.displacementMap ) {
+
+ uniforms.displacementMap.value = material.displacementMap;
+ uniforms.displacementScale.value = material.displacementScale;
+ uniforms.displacementBias.value = material.displacementBias;
+
+ }
+
+ }
+
+ function refreshUniformsLights ( uniforms, lights ) {
+
+ uniforms.ambientLightColor.value = lights.ambient;
+
+ uniforms.directionalLightColor.value = lights.directional.colors;
+ uniforms.directionalLightDirection.value = lights.directional.positions;
+
+ uniforms.pointLightColor.value = lights.point.colors;
+ uniforms.pointLightPosition.value = lights.point.positions;
+ uniforms.pointLightDistance.value = lights.point.distances;
+ uniforms.pointLightDecay.value = lights.point.decays;
+
+ uniforms.spotLightColor.value = lights.spot.colors;
+ uniforms.spotLightPosition.value = lights.spot.positions;
+ uniforms.spotLightDistance.value = lights.spot.distances;
+ uniforms.spotLightDirection.value = lights.spot.directions;
+ uniforms.spotLightAngleCos.value = lights.spot.anglesCos;
+ uniforms.spotLightExponent.value = lights.spot.exponents;
+ uniforms.spotLightDecay.value = lights.spot.decays;
+
+ uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors;
+ uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors;
+ uniforms.hemisphereLightDirection.value = lights.hemi.positions;
+
+ }
+
+ // If uniforms are marked as clean, they don't need to be loaded to the GPU.
+
+ function markUniformsLightsNeedsUpdate ( uniforms, value ) {
+
+ uniforms.ambientLightColor.needsUpdate = value;
+
+ uniforms.directionalLightColor.needsUpdate = value;
+ uniforms.directionalLightDirection.needsUpdate = value;
+
+ uniforms.pointLightColor.needsUpdate = value;
+ uniforms.pointLightPosition.needsUpdate = value;
+ uniforms.pointLightDistance.needsUpdate = value;
+ uniforms.pointLightDecay.needsUpdate = value;
+
+ uniforms.spotLightColor.needsUpdate = value;
+ uniforms.spotLightPosition.needsUpdate = value;
+ uniforms.spotLightDistance.needsUpdate = value;
+ uniforms.spotLightDirection.needsUpdate = value;
+ uniforms.spotLightAngleCos.needsUpdate = value;
+ uniforms.spotLightExponent.needsUpdate = value;
+ uniforms.spotLightDecay.needsUpdate = value;
+
+ uniforms.hemisphereLightSkyColor.needsUpdate = value;
+ uniforms.hemisphereLightGroundColor.needsUpdate = value;
+ uniforms.hemisphereLightDirection.needsUpdate = value;
+
+ }
+
+ function refreshUniformsShadow ( uniforms, lights, camera ) {
+
+ if ( uniforms.shadowMatrix ) {
+
+ var j = 0;
+
+ for ( var i = 0, il = lights.length; i < il; i ++ ) {
+
+ var light = lights[ i ];
+
+ if ( light.castShadow === true ) {
+
+ if ( light instanceof THREE.PointLight || light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) {
+
+ var shadow = light.shadow;
+
+ if ( light instanceof THREE.PointLight ) {
+
+ // for point lights we set the shadow matrix to be a translation-only matrix
+ // equal to inverse of the light's position
+ _vector3.setFromMatrixPosition( light.matrixWorld ).negate();
+ shadow.matrix.identity().setPosition( _vector3 );
+
+ // for point lights we set the sign of the shadowDarkness uniform to be negative
+ uniforms.shadowDarkness.value[ j ] = - shadow.darkness;
+
+ } else {
+
+ uniforms.shadowDarkness.value[ j ] = shadow.darkness;
+
+ }
+
+ uniforms.shadowMatrix.value[ j ] = shadow.matrix;
+ uniforms.shadowMap.value[ j ] = shadow.map;
+ uniforms.shadowMapSize.value[ j ] = shadow.mapSize;
+ uniforms.shadowBias.value[ j ] = shadow.bias;
+
+ j ++;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ // Uniforms (load to GPU)
+
+ function loadUniformsMatrices ( uniforms, object ) {
+
+ _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object.modelViewMatrix.elements );
+
+ if ( uniforms.normalMatrix ) {
+
+ _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object.normalMatrix.elements );
+
+ }
+
+ }
+
+ function getTextureUnit() {
+
+ var textureUnit = _usedTextureUnits;
+
+ if ( textureUnit >= capabilities.maxTextures ) {
+
+ console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures );
+
+ }
+
+ _usedTextureUnits += 1;
+
+ return textureUnit;
+
+ }
+
+ function loadUniformsGeneric ( uniforms ) {
+
+ var texture, textureUnit;
+
+ for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) {
+
+ var uniform = uniforms[ j ][ 0 ];
+
+ // needsUpdate property is not added to all uniforms.
+ if ( uniform.needsUpdate === false ) continue;
+
+ var type = uniform.type;
+ var value = uniform.value;
+ var location = uniforms[ j ][ 1 ];
+
+ switch ( type ) {
+
+ case '1i':
+ _gl.uniform1i( location, value );
+ break;
+
+ case '1f':
+ _gl.uniform1f( location, value );
+ break;
+
+ case '2f':
+ _gl.uniform2f( location, value[ 0 ], value[ 1 ] );
+ break;
+
+ case '3f':
+ _gl.uniform3f( location, value[ 0 ], value[ 1 ], value[ 2 ] );
+ break;
+
+ case '4f':
+ _gl.uniform4f( location, value[ 0 ], value[ 1 ], value[ 2 ], value[ 3 ] );
+ break;
+
+ case '1iv':
+ _gl.uniform1iv( location, value );
+ break;
+
+ case '3iv':
+ _gl.uniform3iv( location, value );
+ break;
+
+ case '1fv':
+ _gl.uniform1fv( location, value );
+ break;
+
+ case '2fv':
+ _gl.uniform2fv( location, value );
+ break;
+
+ case '3fv':
+ _gl.uniform3fv( location, value );
+ break;
+
+ case '4fv':
+ _gl.uniform4fv( location, value );
+ break;
+
+ case 'Matrix3fv':
+ _gl.uniformMatrix3fv( location, false, value );
+ break;
+
+ case 'Matrix4fv':
+ _gl.uniformMatrix4fv( location, false, value );
+ break;
+
+ //
+
+ case 'i':
+
+ // single integer
+ _gl.uniform1i( location, value );
+
+ break;
+
+ case 'f':
+
+ // single float
+ _gl.uniform1f( location, value );
+
+ break;
+
+ case 'v2':
+
+ // single THREE.Vector2
+ _gl.uniform2f( location, value.x, value.y );
+
+ break;
+
+ case 'v3':
+
+ // single THREE.Vector3
+ _gl.uniform3f( location, value.x, value.y, value.z );
+
+ break;
+
+ case 'v4':
+
+ // single THREE.Vector4
+ _gl.uniform4f( location, value.x, value.y, value.z, value.w );
+
+ break;
+
+ case 'c':
+
+ // single THREE.Color
+ _gl.uniform3f( location, value.r, value.g, value.b );
+
+ break;
+
+ case 'iv1':
+
+ // flat array of integers (JS or typed array)
+ _gl.uniform1iv( location, value );
+
+ break;
+
+ case 'iv':
+
+ // flat array of integers with 3 x N size (JS or typed array)
+ _gl.uniform3iv( location, value );
+
+ break;
+
+ case 'fv1':
+
+ // flat array of floats (JS or typed array)
+ _gl.uniform1fv( location, value );
+
+ break;
+
+ case 'fv':
+
+ // flat array of floats with 3 x N size (JS or typed array)
+ _gl.uniform3fv( location, value );
+
+ break;
+
+ case 'v2v':
+
+ // array of THREE.Vector2
+
+ if ( uniform._array === undefined ) {
+
+ uniform._array = new Float32Array( 2 * value.length );
+
+ }
+
+ for ( var i = 0, i2 = 0, il = value.length; i < il; i ++, i2 += 2 ) {
+
+ uniform._array[ i2 + 0 ] = value[ i ].x;
+ uniform._array[ i2 + 1 ] = value[ i ].y;
+
+ }
+
+ _gl.uniform2fv( location, uniform._array );
+
+ break;
+
+ case 'v3v':
+
+ // array of THREE.Vector3
+
+ if ( uniform._array === undefined ) {
+
+ uniform._array = new Float32Array( 3 * value.length );
+
+ }
+
+ for ( var i = 0, i3 = 0, il = value.length; i < il; i ++, i3 += 3 ) {
+
+ uniform._array[ i3 + 0 ] = value[ i ].x;
+ uniform._array[ i3 + 1 ] = value[ i ].y;
+ uniform._array[ i3 + 2 ] = value[ i ].z;
+
+ }
+
+ _gl.uniform3fv( location, uniform._array );
+
+ break;
+
+ case 'v4v':
+
+ // array of THREE.Vector4
+
+ if ( uniform._array === undefined ) {
+
+ uniform._array = new Float32Array( 4 * value.length );
+
+ }
+
+ for ( var i = 0, i4 = 0, il = value.length; i < il; i ++, i4 += 4 ) {
+
+ uniform._array[ i4 + 0 ] = value[ i ].x;
+ uniform._array[ i4 + 1 ] = value[ i ].y;
+ uniform._array[ i4 + 2 ] = value[ i ].z;
+ uniform._array[ i4 + 3 ] = value[ i ].w;
+
+ }
+
+ _gl.uniform4fv( location, uniform._array );
+
+ break;
+
+ case 'm3':
+
+ // single THREE.Matrix3
+ _gl.uniformMatrix3fv( location, false, value.elements );
+
+ break;
+
+ case 'm3v':
+
+ // array of THREE.Matrix3
+
+ if ( uniform._array === undefined ) {
+
+ uniform._array = new Float32Array( 9 * value.length );
+
+ }
+
+ for ( var i = 0, il = value.length; i < il; i ++ ) {
+
+ value[ i ].flattenToArrayOffset( uniform._array, i * 9 );
+
+ }
+
+ _gl.uniformMatrix3fv( location, false, uniform._array );
+
+ break;
+
+ case 'm4':
+
+ // single THREE.Matrix4
+ _gl.uniformMatrix4fv( location, false, value.elements );
+
+ break;
+
+ case 'm4v':
+
+ // array of THREE.Matrix4
+
+ if ( uniform._array === undefined ) {
+
+ uniform._array = new Float32Array( 16 * value.length );
+
+ }
+
+ for ( var i = 0, il = value.length; i < il; i ++ ) {
+
+ value[ i ].flattenToArrayOffset( uniform._array, i * 16 );
+
+ }
+
+ _gl.uniformMatrix4fv( location, false, uniform._array );
+
+ break;
+
+ case 't':
+
+ // single THREE.Texture (2d or cube)
+
+ texture = value;
+ textureUnit = getTextureUnit();
+
+ _gl.uniform1i( location, textureUnit );
+
+ if ( ! texture ) continue;
+
+ if ( texture instanceof THREE.CubeTexture ||
+ ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) {
+
+ // CompressedTexture can have Array in image :/
+
+ setCubeTexture( texture, textureUnit );
+
+ } else if ( texture instanceof THREE.WebGLRenderTargetCube ) {
+
+ setCubeTextureDynamic( texture.texture, textureUnit );
+
+ } else if ( texture instanceof THREE.WebGLRenderTarget ) {
+
+ _this.setTexture( texture.texture, textureUnit );
+
+ } else {
+
+ _this.setTexture( texture, textureUnit );
+
+ }
+
+ break;
+
+ case 'tv':
+
+ // array of THREE.Texture (2d or cube)
+
+ if ( uniform._array === undefined ) {
+
+ uniform._array = [];
+
+ }
+
+ for ( var i = 0, il = uniform.value.length; i < il; i ++ ) {
+
+ uniform._array[ i ] = getTextureUnit();
+
+ }
+
+ _gl.uniform1iv( location, uniform._array );
+
+ for ( var i = 0, il = uniform.value.length; i < il; i ++ ) {
+
+ texture = uniform.value[ i ];
+ textureUnit = uniform._array[ i ];
+
+ if ( ! texture ) continue;
+
+ if ( texture instanceof THREE.CubeTexture ||
+ ( texture.image instanceof Array && texture.image.length === 6 ) ) {
+
+ // CompressedTexture can have Array in image :/
+
+ setCubeTexture( texture, textureUnit );
+
+ } else if ( texture instanceof THREE.WebGLRenderTarget ) {
+
+ _this.setTexture( texture.texture, textureUnit );
+
+ } else if ( texture instanceof THREE.WebGLRenderTargetCube ) {
+
+ setCubeTextureDynamic( texture.texture, textureUnit );
+
+ } else {
+
+ _this.setTexture( texture, textureUnit );
+
+ }
+
+ }
+
+ break;
+
+ default:
+
+ console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type );
+
+ }
+
+ }
+
+ }
+
+ function setColorLinear( array, offset, color, intensity ) {
+
+ array[ offset + 0 ] = color.r * intensity;
+ array[ offset + 1 ] = color.g * intensity;
+ array[ offset + 2 ] = color.b * intensity;
+
+ }
+
+ function setupLights ( lights, camera ) {
+
+ var l, ll, light,
+ r = 0, g = 0, b = 0,
+ color, skyColor, groundColor,
+ intensity,
+ distance,
+
+ zlights = _lights,
+
+ viewMatrix = camera.matrixWorldInverse,
+
+ dirColors = zlights.directional.colors,
+ dirPositions = zlights.directional.positions,
+
+ pointColors = zlights.point.colors,
+ pointPositions = zlights.point.positions,
+ pointDistances = zlights.point.distances,
+ pointDecays = zlights.point.decays,
+
+ spotColors = zlights.spot.colors,
+ spotPositions = zlights.spot.positions,
+ spotDistances = zlights.spot.distances,
+ spotDirections = zlights.spot.directions,
+ spotAnglesCos = zlights.spot.anglesCos,
+ spotExponents = zlights.spot.exponents,
+ spotDecays = zlights.spot.decays,
+
+ hemiSkyColors = zlights.hemi.skyColors,
+ hemiGroundColors = zlights.hemi.groundColors,
+ hemiPositions = zlights.hemi.positions,
+
+ dirLength = 0,
+ pointLength = 0,
+ spotLength = 0,
+ hemiLength = 0,
+
+ dirCount = 0,
+ pointCount = 0,
+ spotCount = 0,
+ hemiCount = 0,
+
+ dirOffset = 0,
+ pointOffset = 0,
+ spotOffset = 0,
+ hemiOffset = 0;
+
+ for ( l = 0, ll = lights.length; l < ll; l ++ ) {
+
+ light = lights[ l ];
+
+ color = light.color;
+ intensity = light.intensity;
+ distance = light.distance;
+
+ if ( light instanceof THREE.AmbientLight ) {
+
+ if ( ! light.visible ) continue;
+
+ r += color.r;
+ g += color.g;
+ b += color.b;
+
+ } else if ( light instanceof THREE.DirectionalLight ) {
+
+ dirCount += 1;
+
+ if ( ! light.visible ) continue;
+
+ _direction.setFromMatrixPosition( light.matrixWorld );
+ _vector3.setFromMatrixPosition( light.target.matrixWorld );
+ _direction.sub( _vector3 );
+ _direction.transformDirection( viewMatrix );
+
+ dirOffset = dirLength * 3;
+
+ dirPositions[ dirOffset + 0 ] = _direction.x;
+ dirPositions[ dirOffset + 1 ] = _direction.y;
+ dirPositions[ dirOffset + 2 ] = _direction.z;
+
+ setColorLinear( dirColors, dirOffset, color, intensity );
+
+ dirLength += 1;
+
+ } else if ( light instanceof THREE.PointLight ) {
+
+ pointCount += 1;
+
+ if ( ! light.visible ) continue;
+
+ pointOffset = pointLength * 3;
+
+ setColorLinear( pointColors, pointOffset, color, intensity );
+
+ _vector3.setFromMatrixPosition( light.matrixWorld );
+ _vector3.applyMatrix4( viewMatrix );
+
+ pointPositions[ pointOffset + 0 ] = _vector3.x;
+ pointPositions[ pointOffset + 1 ] = _vector3.y;
+ pointPositions[ pointOffset + 2 ] = _vector3.z;
+
+ // distance is 0 if decay is 0, because there is no attenuation at all.
+ pointDistances[ pointLength ] = distance;
+ pointDecays[ pointLength ] = ( light.distance === 0 ) ? 0.0 : light.decay;
+
+ pointLength += 1;
+
+ } else if ( light instanceof THREE.SpotLight ) {
+
+ spotCount += 1;
+
+ if ( ! light.visible ) continue;
+
+ spotOffset = spotLength * 3;
+
+ setColorLinear( spotColors, spotOffset, color, intensity );
+
+ _direction.setFromMatrixPosition( light.matrixWorld );
+ _vector3.copy( _direction ).applyMatrix4( viewMatrix );
+
+ spotPositions[ spotOffset + 0 ] = _vector3.x;
+ spotPositions[ spotOffset + 1 ] = _vector3.y;
+ spotPositions[ spotOffset + 2 ] = _vector3.z;
+
+ spotDistances[ spotLength ] = distance;
+
+ _vector3.setFromMatrixPosition( light.target.matrixWorld );
+ _direction.sub( _vector3 );
+ _direction.transformDirection( viewMatrix );
+
+ spotDirections[ spotOffset + 0 ] = _direction.x;
+ spotDirections[ spotOffset + 1 ] = _direction.y;
+ spotDirections[ spotOffset + 2 ] = _direction.z;
+
+ spotAnglesCos[ spotLength ] = Math.cos( light.angle );
+ spotExponents[ spotLength ] = light.exponent;
+ spotDecays[ spotLength ] = ( light.distance === 0 ) ? 0.0 : light.decay;
+
+ spotLength += 1;
+
+ } else if ( light instanceof THREE.HemisphereLight ) {
+
+ hemiCount += 1;
+
+ if ( ! light.visible ) continue;
+
+ _direction.setFromMatrixPosition( light.matrixWorld );
+ _direction.transformDirection( viewMatrix );
+
+ hemiOffset = hemiLength * 3;
+
+ hemiPositions[ hemiOffset + 0 ] = _direction.x;
+ hemiPositions[ hemiOffset + 1 ] = _direction.y;
+ hemiPositions[ hemiOffset + 2 ] = _direction.z;
+
+ skyColor = light.color;
+ groundColor = light.groundColor;
+
+ setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity );
+ setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity );
+
+ hemiLength += 1;
+
+ }
+
+ }
+
+ // null eventual remains from removed lights
+ // (this is to avoid if in shader)
+
+ for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0;
+ for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0;
+ for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0;
+ for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0;
+ for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0;
+
+ zlights.directional.length = dirLength;
+ zlights.point.length = pointLength;
+ zlights.spot.length = spotLength;
+ zlights.hemi.length = hemiLength;
+
+ zlights.ambient[ 0 ] = r;
+ zlights.ambient[ 1 ] = g;
+ zlights.ambient[ 2 ] = b;
+
+ }
+
+ // GL state setting
+
+ this.setFaceCulling = function ( cullFace, frontFaceDirection ) {
+
+ if ( cullFace === THREE.CullFaceNone ) {
+
+ state.disable( _gl.CULL_FACE );
+
+ } else {
+
+ if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) {
+
+ _gl.frontFace( _gl.CW );
+
+ } else {
+
+ _gl.frontFace( _gl.CCW );
+
+ }
+
+ if ( cullFace === THREE.CullFaceBack ) {
+
+ _gl.cullFace( _gl.BACK );
+
+ } else if ( cullFace === THREE.CullFaceFront ) {
+
+ _gl.cullFace( _gl.FRONT );
+
+ } else {
+
+ _gl.cullFace( _gl.FRONT_AND_BACK );
+
+ }
+
+ state.enable( _gl.CULL_FACE );
+
+ }
+
+ };
+
+ // Textures
+
+ function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) {
+
+ var extension;
+
+ if ( isImagePowerOfTwo ) {
+
+ _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) );
+ _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) );
+
+ _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) );
+ _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) );
+
+ } else {
+
+ _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );
+ _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );
+
+ if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) {
+
+ console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.', texture );
+
+ }
+
+ _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );
+ _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );
+
+ if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) {
+
+ console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.', texture );
+
+ }
+
+ }
+
+ extension = extensions.get( 'EXT_texture_filter_anisotropic' );
+
+ if ( extension ) {
+
+ if ( texture.type === THREE.FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return;
+ if ( texture.type === THREE.HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return;
+
+ if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) {
+
+ _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _this.getMaxAnisotropy() ) );
+ properties.get( texture ).__currentAnisotropy = texture.anisotropy;
+
+ }
+
+ }
+
+ }
+
+ function uploadTexture( textureProperties, texture, slot ) {
+
+ if ( textureProperties.__webglInit === undefined ) {
+
+ textureProperties.__webglInit = true;
+
+ texture.addEventListener( 'dispose', onTextureDispose );
+
+ textureProperties.__webglTexture = _gl.createTexture();
+
+ _infoMemory.textures ++;
+
+ }
+
+ state.activeTexture( _gl.TEXTURE0 + slot );
+ state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );
+
+ _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
+ _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
+ _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );
+
+ texture.image = clampToMaxSize( texture.image, capabilities.maxTextureSize );
+
+ if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( texture.image ) === false ) {
+
+ texture.image = makePowerOfTwo( texture.image );
+
+ }
+
+ var image = texture.image,
+ isImagePowerOfTwo = isPowerOfTwo( image ),
+ glFormat = paramThreeToGL( texture.format ),
+ glType = paramThreeToGL( texture.type );
+
+ setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo );
+
+ var mipmap, mipmaps = texture.mipmaps;
+
+ if ( texture instanceof THREE.DataTexture ) {
+
+ // use manually created mipmaps if available
+ // if there are no manual mipmaps
+ // set 0 level mipmap and then use GL to generate other mipmap levels
+
+ if ( mipmaps.length > 0 && isImagePowerOfTwo ) {
+
+ for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+
+ mipmap = mipmaps[ i ];
+ state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
+
+ }
+
+ texture.generateMipmaps = false;
+
+ } else {
+
+ state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data );
+
+ }
+
+ } else if ( texture instanceof THREE.CompressedTexture ) {
+
+ for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+
+ mipmap = mipmaps[ i ];
+
+ if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) {
+
+ if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {
+
+ state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
+
+ } else {
+
+ console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" );
+
+ }
+
+ } else {
+
+ state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
+
+ }
+
+ }
+
+ } else {
+
+ // regular Texture (image, video, canvas)
+
+ // use manually created mipmaps if available
+ // if there are no manual mipmaps
+ // set 0 level mipmap and then use GL to generate other mipmap levels
+
+ if ( mipmaps.length > 0 && isImagePowerOfTwo ) {
+
+ for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+
+ mipmap = mipmaps[ i ];
+ state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap );
+
+ }
+
+ texture.generateMipmaps = false;
+
+ } else {
+
+ state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image );
+
+ }
+
+ }
+
+ if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );
+
+ textureProperties.__version = texture.version;
+
+ if ( texture.onUpdate ) texture.onUpdate( texture );
+
+ }
+
+ this.setTexture = function ( texture, slot ) {
+
+ var textureProperties = properties.get( texture );
+
+ if ( texture.version > 0 && textureProperties.__version !== texture.version ) {
+
+ var image = texture.image;
+
+ if ( image === undefined ) {
+
+ console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture );
+ return;
+
+ }
+
+ if ( image.complete === false ) {
+
+ console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture );
+ return;
+
+ }
+
+ uploadTexture( textureProperties, texture, slot );
+
+ return;
+
+ }
+
+ state.activeTexture( _gl.TEXTURE0 + slot );
+ state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );
+
+ };
+
+ function clampToMaxSize ( image, maxSize ) {
+
+ if ( image.width > maxSize || image.height > maxSize ) {
+
+ // Warning: Scaling through the canvas will only work with images that use
+ // premultiplied alpha.
+
+ var scale = maxSize / Math.max( image.width, image.height );
+
+ var canvas = document.createElement( 'canvas' );
+ canvas.width = Math.floor( image.width * scale );
+ canvas.height = Math.floor( image.height * scale );
+
+ var context = canvas.getContext( '2d' );
+ context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height );
+
+ console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image );
+
+ return canvas;
+
+ }
+
+ return image;
+
+ }
+
+ function isPowerOfTwo( image ) {
+
+ return THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height );
+
+ }
+
+ function textureNeedsPowerOfTwo( texture ) {
+
+ if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) return true;
+ if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) return true;
+
+ return false;
+
+ }
+
+ function makePowerOfTwo( image ) {
+
+ if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement ) {
+
+ var canvas = document.createElement( 'canvas' );
+ canvas.width = THREE.Math.nearestPowerOfTwo( image.width );
+ canvas.height = THREE.Math.nearestPowerOfTwo( image.height );
+
+ var context = canvas.getContext( '2d' );
+ context.drawImage( image, 0, 0, canvas.width, canvas.height );
+
+ console.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image );
+
+ return canvas;
+
+ }
+
+ return image;
+
+ }
+
+ function setCubeTexture ( texture, slot ) {
+
+ var textureProperties = properties.get( texture );
+
+ if ( texture.image.length === 6 ) {
+
+ if ( texture.version > 0 && textureProperties.__version !== texture.version ) {
+
+ if ( ! textureProperties.__image__webglTextureCube ) {
+
+ texture.addEventListener( 'dispose', onTextureDispose );
+
+ textureProperties.__image__webglTextureCube = _gl.createTexture();
+
+ _infoMemory.textures ++;
+
+ }
+
+ state.activeTexture( _gl.TEXTURE0 + slot );
+ state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube );
+
+ _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
+
+ var isCompressed = texture instanceof THREE.CompressedTexture;
+ var isDataTexture = texture.image[ 0 ] instanceof THREE.DataTexture;
+
+ var cubeImage = [];
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ if ( _this.autoScaleCubemaps && ! isCompressed && ! isDataTexture ) {
+
+ cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize );
+
+ } else {
+
+ cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ];
+
+ }
+
+ }
+
+ var image = cubeImage[ 0 ],
+ isImagePowerOfTwo = isPowerOfTwo( image ),
+ glFormat = paramThreeToGL( texture.format ),
+ glType = paramThreeToGL( texture.type );
+
+ setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo );
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ if ( ! isCompressed ) {
+
+ if ( isDataTexture ) {
+
+ state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data );
+
+ } else {
+
+ state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );
+
+ }
+
+ } else {
+
+ var mipmap, mipmaps = cubeImage[ i ].mipmaps;
+
+ for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {
+
+ mipmap = mipmaps[ j ];
+
+ if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) {
+
+ if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {
+
+ state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
+
+ } else {
+
+ console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setCubeTexture()" );
+
+ }
+
+ } else {
+
+ state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ if ( texture.generateMipmaps && isImagePowerOfTwo ) {
+
+ _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
+
+ }
+
+ textureProperties.__version = texture.version;
+
+ if ( texture.onUpdate ) texture.onUpdate( texture );
+
+ } else {
+
+ state.activeTexture( _gl.TEXTURE0 + slot );
+ state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube );
+
+ }
+
+ }
+
+ }
+
+ function setCubeTextureDynamic ( texture, slot ) {
+
+ state.activeTexture( _gl.TEXTURE0 + slot );
+ state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture );
+
+ }
+
+ // Render targets
+
+ function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) {
+
+ _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
+ _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 );
+
+ }
+
+ function setupRenderBuffer ( renderbuffer, renderTarget ) {
+
+ _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer );
+
+ if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {
+
+ _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height );
+ _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
+
+ /* For some reason this is not working. Defaulting to RGBA4.
+ } else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
+
+ _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height );
+ _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
+ */
+
+ } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
+
+ _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height );
+ _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
+
+ } else {
+
+ _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height );
+
+ }
+
+ }
+
+ this.setRenderTarget = function ( renderTarget ) {
+
+ var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube );
+
+ if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) {
+
+ var renderTargetProperties = properties.get( renderTarget );
+ var textureProperties = properties.get( renderTarget.texture );
+
+ if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true;
+ if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true;
+
+ renderTarget.addEventListener( 'dispose', onRenderTargetDispose );
+
+ textureProperties.__webglTexture = _gl.createTexture();
+
+ _infoMemory.textures ++;
+
+ // Setup texture, create render and frame buffers
+
+ var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ),
+ glFormat = paramThreeToGL( renderTarget.texture.format ),
+ glType = paramThreeToGL( renderTarget.texture.type );
+
+ if ( isCube ) {
+
+ renderTargetProperties.__webglFramebuffer = [];
+ renderTargetProperties.__webglRenderbuffer = [];
+
+ state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture );
+
+ setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo );
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer();
+ renderTargetProperties.__webglRenderbuffer[ i ] = _gl.createRenderbuffer();
+ state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
+
+ setupFrameBuffer( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );
+ setupRenderBuffer( renderTargetProperties.__webglRenderbuffer[ i ], renderTarget );
+
+ }
+
+ if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
+
+ } else {
+
+ renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer();
+
+ if ( renderTarget.shareDepthFrom ) {
+
+ renderTargetProperties.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer;
+
+ } else {
+
+ renderTargetProperties.__webglRenderbuffer = _gl.createRenderbuffer();
+
+ }
+
+ state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );
+ setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo );
+
+ state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
+
+ setupFrameBuffer( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D );
+
+ if ( renderTarget.shareDepthFrom ) {
+
+ if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {
+
+ _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTargetProperties.__webglRenderbuffer );
+
+ } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
+
+ _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTargetProperties.__webglRenderbuffer );
+
+ }
+
+ } else {
+
+ setupRenderBuffer( renderTargetProperties.__webglRenderbuffer, renderTarget );
+
+ }
+
+ if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );
+
+ }
+
+ // Release everything
+
+ if ( isCube ) {
+
+ state.bindTexture( _gl.TEXTURE_CUBE_MAP, null );
+
+ } else {
+
+ state.bindTexture( _gl.TEXTURE_2D, null );
+
+ }
+
+ _gl.bindRenderbuffer( _gl.RENDERBUFFER, null );
+ _gl.bindFramebuffer( _gl.FRAMEBUFFER, null );
+
+ }
+
+ var framebuffer, width, height, vx, vy;
+
+ if ( renderTarget ) {
+
+ var renderTargetProperties = properties.get( renderTarget );
+
+ if ( isCube ) {
+
+ framebuffer = renderTargetProperties.__webglFramebuffer[ renderTarget.activeCubeFace ];
+
+ } else {
+
+ framebuffer = renderTargetProperties.__webglFramebuffer;
+
+ }
+
+ width = renderTarget.width;
+ height = renderTarget.height;
+
+ vx = 0;
+ vy = 0;
+
+ } else {
+
+ framebuffer = null;
+
+ width = _viewportWidth;
+ height = _viewportHeight;
+
+ vx = _viewportX;
+ vy = _viewportY;
+
+ }
+
+ if ( framebuffer !== _currentFramebuffer ) {
+
+ _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
+ _gl.viewport( vx, vy, width, height );
+
+ _currentFramebuffer = framebuffer;
+
+ }
+
+ if ( isCube ) {
+
+ var textureProperties = properties.get( renderTarget.texture );
+ _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, 0 );
+
+ }
+
+ _currentWidth = width;
+ _currentHeight = height;
+
+ };
+
+ this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) {
+
+ if ( renderTarget instanceof THREE.WebGLRenderTarget === false ) {
+
+ console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' );
+ return;
+
+ }
+
+ var framebuffer = properties.get( renderTarget ).__webglFramebuffer;
+
+ if ( framebuffer ) {
+
+ var restore = false;
+
+ if ( framebuffer !== _currentFramebuffer ) {
+
+ _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
+
+ restore = true;
+
+ }
+
+ try {
+
+ var texture = renderTarget.texture;
+
+ if ( texture.format !== THREE.RGBAFormat
+ && paramThreeToGL( texture.format ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) {
+
+ console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' );
+ return;
+
+ }
+
+ if ( texture.type !== THREE.UnsignedByteType
+ && paramThreeToGL( texture.type ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE )
+ && ! ( texture.type === THREE.FloatType && extensions.get( 'WEBGL_color_buffer_float' ) )
+ && ! ( texture.type === THREE.HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) {
+
+ console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' );
+ return;
+
+ }
+
+ if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) {
+
+ _gl.readPixels( x, y, width, height, paramThreeToGL( texture.format ), paramThreeToGL( texture.type ), buffer );
+
+ } else {
+
+ console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' );
+
+ }
+
+ } finally {
+
+ if ( restore ) {
+
+ _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer );
+
+ }
+
+ }
+
+ }
+
+ };
+
+ function updateRenderTargetMipmap( renderTarget ) {
+
+ var target = renderTarget instanceof THREE.WebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D;
+ var texture = properties.get( renderTarget.texture ).__webglTexture;
+
+ state.bindTexture( target, texture );
+ _gl.generateMipmap( target );
+ state.bindTexture( target, null );
+
+ }
+
+ // Fallback filters for non-power-of-2 textures
+
+ function filterFallback ( f ) {
+
+ if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) {
+
+ return _gl.NEAREST;
+
+ }
+
+ return _gl.LINEAR;
+
+ }
+
+ // Map three.js constants to WebGL constants
+
+ function paramThreeToGL ( p ) {
+
+ var extension;
+
+ if ( p === THREE.RepeatWrapping ) return _gl.REPEAT;
+ if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE;
+ if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT;
+
+ if ( p === THREE.NearestFilter ) return _gl.NEAREST;
+ if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST;
+ if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR;
+
+ if ( p === THREE.LinearFilter ) return _gl.LINEAR;
+ if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST;
+ if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR;
+
+ if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE;
+ if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4;
+ if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1;
+ if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5;
+
+ if ( p === THREE.ByteType ) return _gl.BYTE;
+ if ( p === THREE.ShortType ) return _gl.SHORT;
+ if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT;
+ if ( p === THREE.IntType ) return _gl.INT;
+ if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT;
+ if ( p === THREE.FloatType ) return _gl.FLOAT;
+
+ extension = extensions.get( 'OES_texture_half_float' );
+
+ if ( extension !== null ) {
+
+ if ( p === THREE.HalfFloatType ) return extension.HALF_FLOAT_OES;
+
+ }
+
+ if ( p === THREE.AlphaFormat ) return _gl.ALPHA;
+ if ( p === THREE.RGBFormat ) return _gl.RGB;
+ if ( p === THREE.RGBAFormat ) return _gl.RGBA;
+ if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE;
+ if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA;
+
+ if ( p === THREE.AddEquation ) return _gl.FUNC_ADD;
+ if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT;
+ if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT;
+
+ if ( p === THREE.ZeroFactor ) return _gl.ZERO;
+ if ( p === THREE.OneFactor ) return _gl.ONE;
+ if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR;
+ if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR;
+ if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA;
+ if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA;
+ if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA;
+ if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA;
+
+ if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR;
+ if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR;
+ if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE;
+
+ extension = extensions.get( 'WEBGL_compressed_texture_s3tc' );
+
+ if ( extension !== null ) {
+
+ if ( p === THREE.RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;
+ if ( p === THREE.RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
+ if ( p === THREE.RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;
+ if ( p === THREE.RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;
+
+ }
+
+ extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' );
+
+ if ( extension !== null ) {
+
+ if ( p === THREE.RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
+ if ( p === THREE.RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
+ if ( p === THREE.RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
+ if ( p === THREE.RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
+
+ }
+
+ extension = extensions.get( 'EXT_blend_minmax' );
+
+ if ( extension !== null ) {
+
+ if ( p === THREE.MinEquation ) return extension.MIN_EXT;
+ if ( p === THREE.MaxEquation ) return extension.MAX_EXT;
+
+ }
+
+ return 0;
+
+ }
+
+ // DEPRECATED
+
+ this.supportsFloatTextures = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' );
+ return extensions.get( 'OES_texture_float' );
+
+ };
+
+ this.supportsHalfFloatTextures = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' );
+ return extensions.get( 'OES_texture_half_float' );
+
+ };
+
+ this.supportsStandardDerivatives = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' );
+ return extensions.get( 'OES_standard_derivatives' );
+
+ };
+
+ this.supportsCompressedTextureS3TC = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' );
+ return extensions.get( 'WEBGL_compressed_texture_s3tc' );
+
+ };
+
+ this.supportsCompressedTexturePVRTC = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' );
+ return extensions.get( 'WEBGL_compressed_texture_pvrtc' );
+
+ };
+
+ this.supportsBlendMinMax = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' );
+ return extensions.get( 'EXT_blend_minmax' );
+
+ };
+
+ this.supportsVertexTextures = function () {
+
+ return capabilities.vertexTextures;
+
+ };
+
+ this.supportsInstancedArrays = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' );
+ return extensions.get( 'ANGLE_instanced_arrays' );
+
+ };
+
+ //
+
+ this.initMaterial = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' );
+
+ };
+
+ this.addPrePlugin = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' );
+
+ };
+
+ this.addPostPlugin = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' );
+
+ };
+
+ this.updateShadowMap = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' );
+
+ };
+
+ Object.defineProperties( this, {
+ shadowMapEnabled: {
+ get: function () {
+
+ return shadowMap.enabled;
+
+ },
+ set: function ( value ) {
+
+ console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' );
+ shadowMap.enabled = value;
+
+ }
+ },
+ shadowMapType: {
+ get: function () {
+
+ return shadowMap.type;
+
+ },
+ set: function ( value ) {
+
+ console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' );
+ shadowMap.type = value;
+
+ }
+ },
+ shadowMapCullFace: {
+ get: function () {
+
+ return shadowMap.cullFace;
+
+ },
+ set: function ( value ) {
+
+ console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace.' );
+ shadowMap.cullFace = value;
+
+ }
+ },
+ shadowMapDebug: {
+ get: function () {
+
+ return shadowMap.debug;
+
+ },
+ set: function ( value ) {
+
+ console.warn( 'THREE.WebGLRenderer: .shadowMapDebug is now .shadowMap.debug.' );
+ shadowMap.debug = value;
+
+ }
+ }
+ } );
+
+};
+
+// File:src/renderers/WebGLRenderTarget.js
+
+/**
+ * @author szimek / https://github.com/szimek/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.WebGLRenderTarget = function ( width, height, options ) {
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.width = width;
+ this.height = height;
+
+ options = options || {};
+
+ if ( options.minFilter === undefined ) options.minFilter = THREE.LinearFilter;
+
+ this.texture = new THREE.Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy );
+
+ this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
+ this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true;
+
+ this.shareDepthFrom = options.shareDepthFrom !== undefined ? options.shareDepthFrom : null;
+
+};
+
+THREE.WebGLRenderTarget.prototype = {
+
+ constructor: THREE.WebGLRenderTarget,
+
+ get wrapS() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' );
+
+ return this.texture.wrapS;
+
+ },
+
+ set wrapS( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' );
+
+ this.texture.wrapS = value;
+
+ },
+
+ get wrapT() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' );
+
+ return this.texture.wrapT;
+
+ },
+
+ set wrapT( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' );
+
+ this.texture.wrapT = value;
+
+ },
+
+ get magFilter() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' );
+
+ return this.texture.magFilter;
+
+ },
+
+ set magFilter( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' );
+
+ this.texture.magFilter = value;
+
+ },
+
+ get minFilter() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' );
+
+ return this.texture.minFilter;
+
+ },
+
+ set minFilter( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' );
+
+ this.texture.minFilter = value;
+
+ },
+
+ get anisotropy() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' );
+
+ return this.texture.anisotropy;
+
+ },
+
+ set anisotropy( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' );
+
+ this.texture.anisotropy = value;
+
+ },
+
+ get offset() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' );
+
+ return this.texture.offset;
+
+ },
+
+ set offset( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' );
+
+ this.texture.offset = value;
+
+ },
+
+ get repeat() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' );
+
+ return this.texture.repeat;
+
+ },
+
+ set repeat( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' );
+
+ this.texture.repeat = value;
+
+ },
+
+ get format() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' );
+
+ return this.texture.format;
+
+ },
+
+ set format( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' );
+
+ this.texture.format = value;
+
+ },
+
+ get type() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' );
+
+ return this.texture.type;
+
+ },
+
+ set type( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' );
+
+ this.texture.type = value;
+
+ },
+
+ get generateMipmaps() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' );
+
+ return this.texture.generateMipmaps;
+
+ },
+
+ set generateMipmaps( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' );
+
+ this.texture.generateMipmaps = value;
+
+ },
+
+ //
+
+ setSize: function ( width, height ) {
+
+ if ( this.width !== width || this.height !== height ) {
+
+ this.width = width;
+ this.height = height;
+
+ this.dispose();
+
+ }
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.width = source.width;
+ this.height = source.height;
+
+ this.texture = source.texture.clone();
+
+ this.depthBuffer = source.depthBuffer;
+ this.stencilBuffer = source.stencilBuffer;
+
+ this.shareDepthFrom = source.shareDepthFrom;
+
+ return this;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.WebGLRenderTarget.prototype );
+
+// File:src/renderers/WebGLRenderTargetCube.js
+
+/**
+ * @author alteredq / http://alteredqualia.com
+ */
+
+THREE.WebGLRenderTargetCube = function ( width, height, options ) {
+
+ THREE.WebGLRenderTarget.call( this, width, height, options );
+
+ this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5
+
+};
+
+THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype );
+THREE.WebGLRenderTargetCube.prototype.constructor = THREE.WebGLRenderTargetCube;
+
+// File:src/renderers/webgl/WebGLBufferRenderer.js
+
+/**
+* @author mrdoob / http://mrdoob.com/
+*/
+
+THREE.WebGLBufferRenderer = function ( _gl, extensions, _infoRender ) {
+
+ var mode;
+
+ function setMode( value ) {
+
+ mode = value;
+
+ }
+
+ function render( start, count ) {
+
+ _gl.drawArrays( mode, start, count );
+
+ _infoRender.calls ++;
+ _infoRender.vertices += count;
+ if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3;
+
+ }
+
+ function renderInstances( geometry ) {
+
+ var extension = extensions.get( 'ANGLE_instanced_arrays' );
+
+ if ( extension === null ) {
+
+ console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
+ return;
+
+ }
+
+ var position = geometry.attributes.position;
+
+ if ( position instanceof THREE.InterleavedBufferAttribute ) {
+
+ extension.drawArraysInstancedANGLE( mode, 0, position.data.count, geometry.maxInstancedCount );
+
+ } else {
+
+ extension.drawArraysInstancedANGLE( mode, 0, position.count, geometry.maxInstancedCount );
+
+ }
+
+ }
+
+ this.setMode = setMode;
+ this.render = render;
+ this.renderInstances = renderInstances;
+
+};
+
+// File:src/renderers/webgl/WebGLIndexedBufferRenderer.js
+
+/**
+* @author mrdoob / http://mrdoob.com/
+*/
+
+THREE.WebGLIndexedBufferRenderer = function ( _gl, extensions, _infoRender ) {
+
+ var mode;
+
+ function setMode( value ) {
+
+ mode = value;
+
+ }
+
+ var type, size;
+
+ function setIndex( index ) {
+
+ if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) {
+
+ type = _gl.UNSIGNED_INT;
+ size = 4;
+
+ } else {
+
+ type = _gl.UNSIGNED_SHORT;
+ size = 2;
+
+ }
+
+ }
+
+ function render( start, count ) {
+
+ _gl.drawElements( mode, count, type, start * size );
+
+ _infoRender.calls ++;
+ _infoRender.vertices += count;
+ if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3;
+
+ }
+
+ function renderInstances( geometry ) {
+
+ var extension = extensions.get( 'ANGLE_instanced_arrays' );
+
+ if ( extension === null ) {
+
+ console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
+ return;
+
+ }
+
+ var index = geometry.index;
+
+ extension.drawElementsInstancedANGLE( mode, index.array.length, type, 0, geometry.maxInstancedCount );
+
+ }
+
+ this.setMode = setMode;
+ this.setIndex = setIndex;
+ this.render = render;
+ this.renderInstances = renderInstances;
+
+};
+
+// File:src/renderers/webgl/WebGLExtensions.js
+
+/**
+* @author mrdoob / http://mrdoob.com/
+*/
+
+THREE.WebGLExtensions = function ( gl ) {
+
+ var extensions = {};
+
+ this.get = function ( name ) {
+
+ if ( extensions[ name ] !== undefined ) {
+
+ return extensions[ name ];
+
+ }
+
+ var extension;
+
+ switch ( name ) {
+
+ case 'EXT_texture_filter_anisotropic':
+ extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );
+ break;
+
+ case 'WEBGL_compressed_texture_s3tc':
+ extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );
+ break;
+
+ case 'WEBGL_compressed_texture_pvrtc':
+ extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );
+ break;
+
+ default:
+ extension = gl.getExtension( name );
+
+ }
+
+ if ( extension === null ) {
+
+ console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' );
+
+ }
+
+ extensions[ name ] = extension;
+
+ return extension;
+
+ };
+
+};
+
+// File:src/renderers/webgl/WebGLCapabilities.js
+
+THREE.WebGLCapabilities = function ( gl, extensions, parameters ) {
+
+ function getMaxPrecision( precision ) {
+
+ if ( precision === 'highp' ) {
+
+ if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 &&
+ gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) {
+
+ return 'highp';
+
+ }
+
+ precision = 'mediump';
+
+ }
+
+ if ( precision === 'mediump' ) {
+
+ if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 &&
+ gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) {
+
+ return 'mediump';
+
+ }
+
+ }
+
+ return 'lowp';
+
+ }
+
+ this.getMaxPrecision = getMaxPrecision;
+
+ this.precision = parameters.precision !== undefined ? parameters.precision : 'highp',
+ this.logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false;
+
+ this.maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );
+ this.maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );
+ this.maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE );
+ this.maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE );
+
+ this.maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );
+ this.maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS );
+ this.maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS );
+ this.maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS );
+
+ this.vertexTextures = this.maxVertexTextures > 0;
+ this.floatFragmentTextures = !! extensions.get( 'OES_texture_float' );
+ this.floatVertexTextures = this.vertexTextures && this.floatFragmentTextures;
+
+ var _maxPrecision = getMaxPrecision( this.precision );
+
+ if ( _maxPrecision !== this.precision ) {
+
+ console.warn( 'THREE.WebGLRenderer:', this.precision, 'not supported, using', _maxPrecision, 'instead.' );
+ this.precision = _maxPrecision;
+
+ }
+
+ if ( this.logarithmicDepthBuffer ) {
+
+ this.logarithmicDepthBuffer = !! extensions.get( 'EXT_frag_depth' );
+
+ }
+
+};
+
+// File:src/renderers/webgl/WebGLGeometries.js
+
+/**
+* @author mrdoob / http://mrdoob.com/
+*/
+
+THREE.WebGLGeometries = function ( gl, properties, info ) {
+
+ var geometries = {};
+
+ function get( object ) {
+
+ var geometry = object.geometry;
+
+ if ( geometries[ geometry.id ] !== undefined ) {
+
+ return geometries[ geometry.id ];
+
+ }
+
+ geometry.addEventListener( 'dispose', onGeometryDispose );
+
+ var buffergeometry;
+
+ if ( geometry instanceof THREE.BufferGeometry ) {
+
+ buffergeometry = geometry;
+
+ } else if ( geometry instanceof THREE.Geometry ) {
+
+ if ( geometry._bufferGeometry === undefined ) {
+
+ geometry._bufferGeometry = new THREE.BufferGeometry().setFromObject( object );
+
+ }
+
+ buffergeometry = geometry._bufferGeometry;
+
+ }
+
+ geometries[ geometry.id ] = buffergeometry;
+
+ info.memory.geometries ++;
+
+ return buffergeometry;
+
+ }
+
+ function onGeometryDispose( event ) {
+
+ var geometry = event.target;
+ var buffergeometry = geometries[ geometry.id ];
+
+ deleteAttributes( buffergeometry.attributes );
+
+ geometry.removeEventListener( 'dispose', onGeometryDispose );
+
+ delete geometries[ geometry.id ];
+
+ var property = properties.get( geometry );
+ if ( property.wireframe ) deleteAttribute( property.wireframe );
+
+ info.memory.geometries --;
+
+ }
+
+ function getAttributeBuffer( attribute ) {
+
+ if ( attribute instanceof THREE.InterleavedBufferAttribute ) {
+
+ return properties.get( attribute.data ).__webglBuffer;
+
+ }
+
+ return properties.get( attribute ).__webglBuffer;
+
+ }
+
+ function deleteAttribute( attribute ) {
+
+ var buffer = getAttributeBuffer( attribute );
+
+ if ( buffer !== undefined ) {
+
+ gl.deleteBuffer( buffer );
+ removeAttributeBuffer( attribute );
+
+ }
+
+ }
+
+ function deleteAttributes( attributes ) {
+
+ for ( var name in attributes ) {
+
+ deleteAttribute( attributes[ name ] );
+
+ }
+
+ }
+
+ function removeAttributeBuffer( attribute ) {
+
+ if ( attribute instanceof THREE.InterleavedBufferAttribute ) {
+
+ properties.delete( attribute.data );
+
+ } else {
+
+ properties.delete( attribute );
+
+ }
+
+ }
+
+ this.get = get;
+
+};
+
+// File:src/renderers/webgl/WebGLObjects.js
+
+/**
+* @author mrdoob / http://mrdoob.com/
+*/
+
+THREE.WebGLObjects = function ( gl, properties, info ) {
+
+ var geometries = new THREE.WebGLGeometries( gl, properties, info );
+
+ //
+
+ function update( object ) {
+
+ // TODO: Avoid updating twice (when using shadowMap). Maybe add frame counter.
+
+ var geometry = geometries.get( object );
+
+ if ( object.geometry instanceof THREE.Geometry ) {
+
+ geometry.updateFromObject( object );
+
+ }
+
+ var index = geometry.index;
+ var attributes = geometry.attributes;
+
+ if ( index !== null ) {
+
+ updateAttribute( index, gl.ELEMENT_ARRAY_BUFFER );
+
+ }
+
+ for ( var name in attributes ) {
+
+ updateAttribute( attributes[ name ], gl.ARRAY_BUFFER );
+
+ }
+
+ // morph targets
+
+ var morphAttributes = geometry.morphAttributes;
+
+ for ( var name in morphAttributes ) {
+
+ var array = morphAttributes[ name ];
+
+ for ( var i = 0, l = array.length; i < l; i ++ ) {
+
+ updateAttribute( array[ i ], gl.ARRAY_BUFFER );
+
+ }
+
+ }
+
+ return geometry;
+
+ }
+
+ function updateAttribute( attribute, bufferType ) {
+
+ var data = ( attribute instanceof THREE.InterleavedBufferAttribute ) ? attribute.data : attribute;
+
+ var attributeProperties = properties.get( data );
+
+ if ( attributeProperties.__webglBuffer === undefined ) {
+
+ createBuffer( attributeProperties, data, bufferType );
+
+ } else if ( attributeProperties.version !== data.version ) {
+
+ updateBuffer( attributeProperties, data, bufferType );
+
+ }
+
+ }
+
+ function createBuffer( attributeProperties, data, bufferType ) {
+
+ attributeProperties.__webglBuffer = gl.createBuffer();
+ gl.bindBuffer( bufferType, attributeProperties.__webglBuffer );
+
+ var usage = data.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW;
+
+ gl.bufferData( bufferType, data.array, usage );
+
+ attributeProperties.version = data.version;
+
+ }
+
+ function updateBuffer( attributeProperties, data, bufferType ) {
+
+ gl.bindBuffer( bufferType, attributeProperties.__webglBuffer );
+
+ if ( data.dynamic === false || data.updateRange.count === - 1 ) {
+
+ // Not using update ranges
+
+ gl.bufferSubData( bufferType, 0, data.array );
+
+ } else if ( data.updateRange.count === 0 ) {
+
+ console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' );
+
+ } else {
+
+ gl.bufferSubData( bufferType, data.updateRange.offset * data.array.BYTES_PER_ELEMENT,
+ data.array.subarray( data.updateRange.offset, data.updateRange.offset + data.updateRange.count ) );
+
+ data.updateRange.count = 0; // reset range
+
+ }
+
+ attributeProperties.version = data.version;
+
+ }
+
+ function getAttributeBuffer( attribute ) {
+
+ if ( attribute instanceof THREE.InterleavedBufferAttribute ) {
+
+ return properties.get( attribute.data ).__webglBuffer;
+
+ }
+
+ return properties.get( attribute ).__webglBuffer;
+
+ }
+
+ function getWireframeAttribute( geometry ) {
+
+ var property = properties.get( geometry );
+
+ if ( property.wireframe !== undefined ) {
+
+ return property.wireframe;
+
+ }
+
+ var indices = [];
+
+ var index = geometry.index;
+ var attributes = geometry.attributes;
+ var position = attributes.position;
+
+ // console.time( 'wireframe' );
+
+ if ( index !== null ) {
+
+ var edges = {};
+ var array = index.array;
+
+ for ( var i = 0, l = array.length; i < l; i += 3 ) {
+
+ var a = array[ i + 0 ];
+ var b = array[ i + 1 ];
+ var c = array[ i + 2 ];
+
+ if ( checkEdge( edges, a, b ) ) indices.push( a, b );
+ if ( checkEdge( edges, b, c ) ) indices.push( b, c );
+ if ( checkEdge( edges, c, a ) ) indices.push( c, a );
+
+ }
+
+ } else {
+
+ var array = attributes.position.array;
+
+ for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) {
+
+ var a = i + 0;
+ var b = i + 1;
+ var c = i + 2;
+
+ indices.push( a, b, b, c, c, a );
+
+ }
+
+ }
+
+ // console.timeEnd( 'wireframe' );
+
+ var TypeArray = position.count > 65535 ? Uint32Array : Uint16Array;
+ var attribute = new THREE.BufferAttribute( new TypeArray( indices ), 1 );
+
+ updateAttribute( attribute, gl.ELEMENT_ARRAY_BUFFER );
+
+ property.wireframe = attribute;
+
+ return attribute;
+
+ }
+
+ function checkEdge( edges, a, b ) {
+
+ if ( a > b ) {
+
+ var tmp = a;
+ a = b;
+ b = tmp;
+
+ }
+
+ var list = edges[ a ];
+
+ if ( list === undefined ) {
+
+ edges[ a ] = [ b ];
+ return true;
+
+ } else if ( list.indexOf( b ) === -1 ) {
+
+ list.push( b );
+ return true;
+
+ }
+
+ return false;
+
+ }
+
+ this.getAttributeBuffer = getAttributeBuffer;
+ this.getWireframeAttribute = getWireframeAttribute;
+
+ this.update = update;
+
+};
+
+// File:src/renderers/webgl/WebGLProgram.js
+
+THREE.WebGLProgram = ( function () {
+
+ var programIdCount = 0;
+
+ function generateDefines( defines ) {
+
+ var chunks = [];
+
+ for ( var name in defines ) {
+
+ var value = defines[ name ];
+
+ if ( value === false ) continue;
+
+ chunks.push( '#define ' + name + ' ' + value );
+
+ }
+
+ return chunks.join( '\n' );
+
+ }
+
+ function fetchUniformLocations( gl, program, identifiers ) {
+
+ var uniforms = {};
+
+ var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );
+
+ for ( var i = 0; i < n; i ++ ) {
+
+ var info = gl.getActiveUniform( program, i );
+ var name = info.name;
+ var location = gl.getUniformLocation( program, name );
+
+ // console.log("THREE.WebGLProgram: ACTIVE UNIFORM:", name);
+
+ var suffixPos = name.lastIndexOf( '[0]' );
+ if ( suffixPos !== - 1 && suffixPos === name.length - 3 ) {
+
+ uniforms[ name.substr( 0, suffixPos ) ] = location;
+
+ }
+
+ uniforms[ name ] = location;
+
+ }
+
+ return uniforms;
+
+ }
+
+ function fetchAttributeLocations( gl, program, identifiers ) {
+
+ var attributes = {};
+
+ var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES );
+
+ for ( var i = 0; i < n; i ++ ) {
+
+ var info = gl.getActiveAttrib( program, i );
+ var name = info.name;
+
+ // console.log("THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i );
+
+ attributes[ name ] = gl.getAttribLocation( program, name );
+
+ }
+
+ return attributes;
+
+ }
+
+ function filterEmptyLine( string ) {
+
+ return string !== '';
+
+ }
+
+ return function WebGLProgram( renderer, code, material, parameters ) {
+
+ var gl = renderer.context;
+
+ var defines = material.defines;
+
+ var vertexShader = material.__webglShader.vertexShader;
+ var fragmentShader = material.__webglShader.fragmentShader;
+
+ var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC';
+
+ if ( parameters.shadowMapType === THREE.PCFShadowMap ) {
+
+ shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';
+
+ } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) {
+
+ shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';
+
+ }
+
+ var envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
+ var envMapModeDefine = 'ENVMAP_MODE_REFLECTION';
+ var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';
+
+ if ( parameters.envMap ) {
+
+ switch ( material.envMap.mapping ) {
+
+ case THREE.CubeReflectionMapping:
+ case THREE.CubeRefractionMapping:
+ envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
+ break;
+
+ case THREE.EquirectangularReflectionMapping:
+ case THREE.EquirectangularRefractionMapping:
+ envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC';
+ break;
+
+ case THREE.SphericalReflectionMapping:
+ envMapTypeDefine = 'ENVMAP_TYPE_SPHERE';
+ break;
+
+ }
+
+ switch ( material.envMap.mapping ) {
+
+ case THREE.CubeRefractionMapping:
+ case THREE.EquirectangularRefractionMapping:
+ envMapModeDefine = 'ENVMAP_MODE_REFRACTION';
+ break;
+
+ }
+
+ switch ( material.combine ) {
+
+ case THREE.MultiplyOperation:
+ envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';
+ break;
+
+ case THREE.MixOperation:
+ envMapBlendingDefine = 'ENVMAP_BLENDING_MIX';
+ break;
+
+ case THREE.AddOperation:
+ envMapBlendingDefine = 'ENVMAP_BLENDING_ADD';
+ break;
+
+ }
+
+ }
+
+ var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0;
+
+ // console.log( 'building new program ' );
+
+ //
+
+ var customDefines = generateDefines( defines );
+
+ //
+
+ var program = gl.createProgram();
+
+ var prefixVertex, prefixFragment;
+
+ if ( material instanceof THREE.RawShaderMaterial ) {
+
+ prefixVertex = '';
+ prefixFragment = '';
+
+ } else {
+
+ prefixVertex = [
+
+ 'precision ' + parameters.precision + ' float;',
+ 'precision ' + parameters.precision + ' int;',
+
+ '#define SHADER_NAME ' + material.__webglShader.name,
+
+ customDefines,
+
+ parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '',
+
+ renderer.gammaInput ? '#define GAMMA_INPUT' : '',
+ renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '',
+ '#define GAMMA_FACTOR ' + gammaFactorDefine,
+
+ '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights,
+ '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights,
+ '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights,
+ '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights,
+
+ '#define MAX_SHADOWS ' + parameters.maxShadows,
+
+ '#define MAX_BONES ' + parameters.maxBones,
+
+ parameters.map ? '#define USE_MAP' : '',
+ parameters.envMap ? '#define USE_ENVMAP' : '',
+ parameters.envMap ? '#define ' + envMapModeDefine : '',
+ parameters.lightMap ? '#define USE_LIGHTMAP' : '',
+ parameters.aoMap ? '#define USE_AOMAP' : '',
+ parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
+ parameters.bumpMap ? '#define USE_BUMPMAP' : '',
+ parameters.normalMap ? '#define USE_NORMALMAP' : '',
+ parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '',
+ parameters.specularMap ? '#define USE_SPECULARMAP' : '',
+ parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
+ parameters.vertexColors ? '#define USE_COLOR' : '',
+
+ parameters.flatShading ? '#define FLAT_SHADED' : '',
+
+ parameters.skinning ? '#define USE_SKINNING' : '',
+ parameters.useVertexTexture ? '#define BONE_TEXTURE' : '',
+
+ parameters.morphTargets ? '#define USE_MORPHTARGETS' : '',
+ parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '',
+ parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
+ parameters.flipSided ? '#define FLIP_SIDED' : '',
+
+ parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
+ parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
+ parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '',
+ parameters.pointLightShadows > 0 ? '#define POINT_LIGHT_SHADOWS' : '',
+
+ parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',
+
+ parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
+ parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',
+
+
+ 'uniform mat4 modelMatrix;',
+ 'uniform mat4 modelViewMatrix;',
+ 'uniform mat4 projectionMatrix;',
+ 'uniform mat4 viewMatrix;',
+ 'uniform mat3 normalMatrix;',
+ 'uniform vec3 cameraPosition;',
+
+ 'attribute vec3 position;',
+ 'attribute vec3 normal;',
+ 'attribute vec2 uv;',
+
+ '#ifdef USE_COLOR',
+
+ ' attribute vec3 color;',
+
+ '#endif',
+
+ '#ifdef USE_MORPHTARGETS',
+
+ ' attribute vec3 morphTarget0;',
+ ' attribute vec3 morphTarget1;',
+ ' attribute vec3 morphTarget2;',
+ ' attribute vec3 morphTarget3;',
+
+ ' #ifdef USE_MORPHNORMALS',
+
+ ' attribute vec3 morphNormal0;',
+ ' attribute vec3 morphNormal1;',
+ ' attribute vec3 morphNormal2;',
+ ' attribute vec3 morphNormal3;',
+
+ ' #else',
+
+ ' attribute vec3 morphTarget4;',
+ ' attribute vec3 morphTarget5;',
+ ' attribute vec3 morphTarget6;',
+ ' attribute vec3 morphTarget7;',
+
+ ' #endif',
+
+ '#endif',
+
+ '#ifdef USE_SKINNING',
+
+ ' attribute vec4 skinIndex;',
+ ' attribute vec4 skinWeight;',
+
+ '#endif',
+
+ '\n'
+
+ ].filter( filterEmptyLine ).join( '\n' );
+
+ prefixFragment = [
+
+ parameters.bumpMap || parameters.normalMap || parameters.flatShading || material.derivatives ? '#extension GL_OES_standard_derivatives : enable' : '',
+ parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '',
+
+ 'precision ' + parameters.precision + ' float;',
+ 'precision ' + parameters.precision + ' int;',
+
+ '#define SHADER_NAME ' + material.__webglShader.name,
+
+ customDefines,
+
+ '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights,
+ '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights,
+ '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights,
+ '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights,
+
+ '#define MAX_SHADOWS ' + parameters.maxShadows,
+
+ parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '',
+
+ renderer.gammaInput ? '#define GAMMA_INPUT' : '',
+ renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '',
+ '#define GAMMA_FACTOR ' + gammaFactorDefine,
+
+ ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',
+ ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',
+
+ parameters.map ? '#define USE_MAP' : '',
+ parameters.envMap ? '#define USE_ENVMAP' : '',
+ parameters.envMap ? '#define ' + envMapTypeDefine : '',
+ parameters.envMap ? '#define ' + envMapModeDefine : '',
+ parameters.envMap ? '#define ' + envMapBlendingDefine : '',
+ parameters.lightMap ? '#define USE_LIGHTMAP' : '',
+ parameters.aoMap ? '#define USE_AOMAP' : '',
+ parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
+ parameters.bumpMap ? '#define USE_BUMPMAP' : '',
+ parameters.normalMap ? '#define USE_NORMALMAP' : '',
+ parameters.specularMap ? '#define USE_SPECULARMAP' : '',
+ parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
+ parameters.vertexColors ? '#define USE_COLOR' : '',
+
+ parameters.flatShading ? '#define FLAT_SHADED' : '',
+
+ parameters.metal ? '#define METAL' : '',
+ parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
+ parameters.flipSided ? '#define FLIP_SIDED' : '',
+
+ parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
+ parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
+ parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '',
+ parameters.pointLightShadows > 0 ? '#define POINT_LIGHT_SHADOWS' : '',
+
+ parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
+ parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',
+
+ 'uniform mat4 viewMatrix;',
+ 'uniform vec3 cameraPosition;',
+
+ '\n'
+
+ ].filter( filterEmptyLine ).join( '\n' );
+
+ }
+
+ var vertexGlsl = prefixVertex + vertexShader;
+ var fragmentGlsl = prefixFragment + fragmentShader;
+
+ var glVertexShader = THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );
+ var glFragmentShader = THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );
+
+ gl.attachShader( program, glVertexShader );
+ gl.attachShader( program, glFragmentShader );
+
+ // Force a particular attribute to index 0.
+
+ if ( material.index0AttributeName !== undefined ) {
+
+ gl.bindAttribLocation( program, 0, material.index0AttributeName );
+
+ } else if ( parameters.morphTargets === true ) {
+
+ // programs with morphTargets displace position out of attribute 0
+ gl.bindAttribLocation( program, 0, 'position' );
+
+ }
+
+ gl.linkProgram( program );
+
+ var programLog = gl.getProgramInfoLog( program );
+ var vertexLog = gl.getShaderInfoLog( glVertexShader );
+ var fragmentLog = gl.getShaderInfoLog( glFragmentShader );
+
+ var runnable = true;
+ var haveDiagnostics = true;
+
+ if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) {
+
+ runnable = false;
+
+ console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog );
+
+ } else if ( programLog !== '' ) {
+
+ console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog );
+
+ } else if ( vertexLog === '' || fragmentLog === '' ) {
+
+ haveDiagnostics = false;
+
+ }
+
+ if ( haveDiagnostics ) {
+
+ this.diagnostics = {
+
+ runnable: runnable,
+ material: material,
+
+ programLog: programLog,
+
+ vertexShader: {
+
+ log: vertexLog,
+ prefix: prefixVertex
+
+ },
+
+ fragmentShader: {
+
+ log: fragmentLog,
+ prefix: prefixFragment
+
+ }
+
+ };
+
+ }
+
+ // clean up
+
+ gl.deleteShader( glVertexShader );
+ gl.deleteShader( glFragmentShader );
+
+ // set up caching for uniform locations
+
+ var cachedUniforms;
+
+ this.getUniforms = function() {
+
+ if ( cachedUniforms === undefined ) {
+
+ cachedUniforms = fetchUniformLocations( gl, program );
+
+ }
+
+ return cachedUniforms;
+
+ };
+
+ // set up caching for attribute locations
+
+ var cachedAttributes;
+
+ this.getAttributes = function() {
+
+ if ( cachedAttributes === undefined ) {
+
+ cachedAttributes = fetchAttributeLocations( gl, program );
+
+ }
+
+ return cachedAttributes;
+
+ };
+
+ // free resource
+
+ this.destroy = function() {
+
+ gl.deleteProgram( program );
+ this.program = undefined;
+
+ };
+
+ // DEPRECATED
+
+ Object.defineProperties( this, {
+
+ uniforms: {
+ get: function() {
+
+ console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' );
+ return this.getUniforms();
+
+ }
+ },
+
+ attributes: {
+ get: function() {
+
+ console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' );
+ return this.getAttributes();
+
+ }
+ }
+
+ } );
+
+
+ //
+
+ this.id = programIdCount ++;
+ this.code = code;
+ this.usedTimes = 1;
+ this.program = program;
+ this.vertexShader = glVertexShader;
+ this.fragmentShader = glFragmentShader;
+
+ return this;
+
+ };
+
+} )();
+
+// File:src/renderers/webgl/WebGLPrograms.js
+
+THREE.WebGLPrograms = function ( renderer, capabilities ) {
+
+ var programs = [];
+
+ var shaderIDs = {
+ MeshDepthMaterial: 'depth',
+ MeshNormalMaterial: 'normal',
+ MeshBasicMaterial: 'basic',
+ MeshLambertMaterial: 'lambert',
+ MeshPhongMaterial: 'phong',
+ LineBasicMaterial: 'basic',
+ LineDashedMaterial: 'dashed',
+ PointsMaterial: 'points'
+ };
+
+ var parameterNames = [
+ "precision", "supportsVertexTextures", "map", "envMap", "envMapMode",
+ "lightMap", "aoMap", "emissiveMap", "bumpMap", "normalMap", "displacementMap", "specularMap",
+ "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp",
+ "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
+ "maxBones", "useVertexTexture", "morphTargets", "morphNormals",
+ "maxMorphTargets", "maxMorphNormals", "maxDirLights", "maxPointLights",
+ "maxSpotLights", "maxHemiLights", "maxShadows", "shadowMapEnabled", "pointLightShadows",
+ "shadowMapType", "shadowMapDebug", "alphaTest", "metal", "doubleSided",
+ "flipSided"
+ ];
+
+
+ function allocateBones ( object ) {
+
+ if ( capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture ) {
+
+ return 1024;
+
+ } else {
+
+ // default for when object is not specified
+ // ( for example when prebuilding shader to be used with multiple objects )
+ //
+ // - leave some extra space for other uniforms
+ // - limit here is ANGLE's 254 max uniform vectors
+ // (up to 54 should be safe)
+
+ var nVertexUniforms = capabilities.maxVertexUniforms;
+ var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 );
+
+ var maxBones = nVertexMatrices;
+
+ if ( object !== undefined && object instanceof THREE.SkinnedMesh ) {
+
+ maxBones = Math.min( object.skeleton.bones.length, maxBones );
+
+ if ( maxBones < object.skeleton.bones.length ) {
+
+ console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' );
+
+ }
+
+ }
+
+ return maxBones;
+
+ }
+
+ }
+
+ function allocateLights( lights ) {
+
+ var dirLights = 0;
+ var pointLights = 0;
+ var spotLights = 0;
+ var hemiLights = 0;
+
+ for ( var l = 0, ll = lights.length; l < ll; l ++ ) {
+
+ var light = lights[ l ];
+
+ if ( light.visible === false ) continue;
+
+ if ( light instanceof THREE.DirectionalLight ) dirLights ++;
+ if ( light instanceof THREE.PointLight ) pointLights ++;
+ if ( light instanceof THREE.SpotLight ) spotLights ++;
+ if ( light instanceof THREE.HemisphereLight ) hemiLights ++;
+
+ }
+
+ return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights };
+
+ }
+
+ function allocateShadows( lights ) {
+
+ var maxShadows = 0;
+ var pointLightShadows = 0;
+
+ for ( var l = 0, ll = lights.length; l < ll; l ++ ) {
+
+ var light = lights[ l ];
+
+ if ( ! light.castShadow ) continue;
+
+ if ( light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) maxShadows ++;
+ if ( light instanceof THREE.PointLight ) {
+
+ maxShadows ++;
+ pointLightShadows ++;
+
+ }
+
+ }
+
+ return { 'maxShadows': maxShadows, 'pointLightShadows': pointLightShadows };
+
+ }
+
+ this.getParameters = function ( material, lights, fog, object ) {
+
+ var shaderID = shaderIDs[ material.type ];
+ // heuristics to create shader parameters according to lights in the scene
+ // (not to blow over maxLights budget)
+
+ var maxLightCount = allocateLights( lights );
+ var allocatedShadows = allocateShadows( lights );
+ var maxBones = allocateBones( object );
+ var precision = renderer.getPrecision();
+
+ if ( material.precision !== null ) {
+
+ precision = capabilities.getMaxPrecision( material.precision );
+
+ if ( precision !== material.precision ) {
+
+ console.warn( 'THREE.WebGLRenderer.initMaterial:', material.precision, 'not supported, using', precision, 'instead.' );
+
+ }
+
+ }
+
+ var parameters = {
+
+ shaderID: shaderID,
+
+ precision: precision,
+ supportsVertexTextures: capabilities.vertexTextures,
+
+ map: !! material.map,
+ envMap: !! material.envMap,
+ envMapMode: material.envMap && material.envMap.mapping,
+ lightMap: !! material.lightMap,
+ aoMap: !! material.aoMap,
+ emissiveMap: !! material.emissiveMap,
+ bumpMap: !! material.bumpMap,
+ normalMap: !! material.normalMap,
+ displacementMap: !! material.displacementMap,
+ specularMap: !! material.specularMap,
+ alphaMap: !! material.alphaMap,
+
+ combine: material.combine,
+
+ vertexColors: material.vertexColors,
+
+ fog: fog,
+ useFog: material.fog,
+ fogExp: fog instanceof THREE.FogExp2,
+
+ flatShading: material.shading === THREE.FlatShading,
+
+ sizeAttenuation: material.sizeAttenuation,
+ logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer,
+
+ skinning: material.skinning,
+ maxBones: maxBones,
+ useVertexTexture: capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture,
+
+ morphTargets: material.morphTargets,
+ morphNormals: material.morphNormals,
+ maxMorphTargets: renderer.maxMorphTargets,
+ maxMorphNormals: renderer.maxMorphNormals,
+
+ maxDirLights: maxLightCount.directional,
+ maxPointLights: maxLightCount.point,
+ maxSpotLights: maxLightCount.spot,
+ maxHemiLights: maxLightCount.hemi,
+
+ maxShadows: allocatedShadows.maxShadows,
+ pointLightShadows: allocatedShadows.pointLightShadows,
+ shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && allocatedShadows.maxShadows > 0,
+ shadowMapType: renderer.shadowMap.type,
+ shadowMapDebug: renderer.shadowMap.debug,
+
+ alphaTest: material.alphaTest,
+ metal: material.metal,
+ doubleSided: material.side === THREE.DoubleSide,
+ flipSided: material.side === THREE.BackSide
+
+ };
+
+ return parameters;
+
+ };
+
+ this.getProgramCode = function ( material, parameters ) {
+
+ var chunks = [];
+
+ if ( parameters.shaderID ) {
+
+ chunks.push( parameters.shaderID );
+
+ } else {
+
+ chunks.push( material.fragmentShader );
+ chunks.push( material.vertexShader );
+
+ }
+
+ if ( material.defines !== undefined ) {
+
+ for ( var name in material.defines ) {
+
+ chunks.push( name );
+ chunks.push( material.defines[ name ] );
+
+ }
+
+ }
+
+ for ( var i = 0; i < parameterNames.length; i ++ ) {
+
+ var parameterName = parameterNames[ i ];
+ chunks.push( parameterName );
+ chunks.push( parameters[ parameterName ] );
+
+ }
+
+ return chunks.join();
+
+ };
+
+ this.acquireProgram = function ( material, parameters, code ) {
+
+ var program;
+
+ // Check if code has been already compiled
+ for ( var p = 0, pl = programs.length; p < pl; p ++ ) {
+
+ var programInfo = programs[ p ];
+
+ if ( programInfo.code === code ) {
+
+ program = programInfo;
+ ++ program.usedTimes;
+
+ break;
+
+ }
+
+ }
+
+ if ( program === undefined ) {
+
+ program = new THREE.WebGLProgram( renderer, code, material, parameters );
+ programs.push( program );
+
+ }
+
+ return program;
+
+ };
+
+ this.releaseProgram = function( program ) {
+
+ if ( -- program.usedTimes === 0 ) {
+
+ // Remove from unordered set
+ var i = programs.indexOf( program );
+ programs[ i ] = programs[ programs.length - 1 ];
+ programs.pop();
+
+ // Free WebGL resources
+ program.destroy();
+
+ }
+
+ };
+
+ // Exposed for resource monitoring & error feedback via renderer.info:
+ this.programs = programs;
+
+};
+
+// File:src/renderers/webgl/WebGLProperties.js
+
+/**
+* @author fordacious / fordacious.github.io
+*/
+
+THREE.WebGLProperties = function () {
+
+ var properties = {};
+
+ this.get = function ( object ) {
+
+ var uuid = object.uuid;
+ var map = properties[ uuid ];
+
+ if ( map === undefined ) {
+
+ map = {};
+ properties[ uuid ] = map;
+
+ }
+
+ return map;
+
+ };
+
+ this.delete = function ( object ) {
+
+ delete properties[ object.uuid ];
+
+ };
+
+ this.clear = function () {
+
+ properties = {};
+
+ };
+
+};
+
+// File:src/renderers/webgl/WebGLShader.js
+
+THREE.WebGLShader = ( function () {
+
+ function addLineNumbers( string ) {
+
+ var lines = string.split( '\n' );
+
+ for ( var i = 0; i < lines.length; i ++ ) {
+
+ lines[ i ] = ( i + 1 ) + ': ' + lines[ i ];
+
+ }
+
+ return lines.join( '\n' );
+
+ }
+
+ return function WebGLShader( gl, type, string ) {
+
+ var shader = gl.createShader( type );
+
+ gl.shaderSource( shader, string );
+ gl.compileShader( shader );
+
+ if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) {
+
+ console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' );
+
+ }
+
+ if ( gl.getShaderInfoLog( shader ) !== '' ) {
+
+ console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) );
+
+ }
+
+ // --enable-privileged-webgl-extension
+ // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) );
+
+ return shader;
+
+ };
+
+} )();
+
+// File:src/renderers/webgl/WebGLShadowMap.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
+
+ var _gl = _renderer.context,
+ _state = _renderer.state,
+ _frustum = new THREE.Frustum(),
+ _projScreenMatrix = new THREE.Matrix4(),
+
+ _min = new THREE.Vector3(),
+ _max = new THREE.Vector3(),
+
+ _lookTarget = new THREE.Vector3(),
+ _lightPositionWorld = new THREE.Vector3(),
+
+ _renderList = [],
+
+ _MorphingFlag = 1,
+ _SkinningFlag = 2,
+
+ _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1,
+
+ _depthMaterials = new Array( _NumberOfMaterialVariants ),
+ _distanceMaterials = new Array( _NumberOfMaterialVariants );
+
+ var cubeDirections = [
+ new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( - 1, 0, 0 ), new THREE.Vector3( 0, 0, 1 ),
+ new THREE.Vector3( 0, 0, - 1 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, - 1, 0 )
+ ];
+
+ var cubeUps = [
+ new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ),
+ new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ), new THREE.Vector3( 0, 0, - 1 )
+ ];
+
+ var cube2DViewPorts = [
+ new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(),
+ new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4()
+ ];
+
+ var _vector4 = new THREE.Vector4();
+
+ // init
+
+ var depthShader = THREE.ShaderLib[ "depthRGBA" ];
+ var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
+
+ var distanceShader = THREE.ShaderLib[ "distanceRGBA" ];
+ var distanceUniforms = THREE.UniformsUtils.clone( distanceShader.uniforms );
+
+ for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) {
+
+ var useMorphing = ( i & _MorphingFlag ) !== 0;
+ var useSkinning = ( i & _SkinningFlag ) !== 0;
+
+ var depthMaterial = new THREE.ShaderMaterial( {
+ uniforms: depthUniforms,
+ vertexShader: depthShader.vertexShader,
+ fragmentShader: depthShader.fragmentShader,
+ morphTargets: useMorphing,
+ skinning: useSkinning
+ } );
+
+ depthMaterial._shadowPass = true;
+
+ _depthMaterials[ i ] = depthMaterial;
+
+ var distanceMaterial = new THREE.ShaderMaterial( {
+ uniforms: distanceUniforms,
+ vertexShader: distanceShader.vertexShader,
+ fragmentShader: distanceShader.fragmentShader,
+ morphTargets: useMorphing,
+ skinning: useSkinning
+ } );
+
+ distanceMaterial._shadowPass = true;
+
+ _distanceMaterials[ i ] = distanceMaterial;
+
+ }
+
+ //
+
+ var scope = this;
+
+ this.enabled = false;
+
+ this.autoUpdate = true;
+ this.needsUpdate = false;
+
+ this.type = THREE.PCFShadowMap;
+ this.cullFace = THREE.CullFaceFront;
+
+ this.render = function ( scene ) {
+
+ var faceCount, isPointLight;
+
+ if ( scope.enabled === false ) return;
+ if ( scope.autoUpdate === false && scope.needsUpdate === false ) return;
+
+ // Set GL state for depth map.
+ _gl.clearColor( 1, 1, 1, 1 );
+ _state.disable( _gl.BLEND );
+ _state.enable( _gl.CULL_FACE );
+ _gl.frontFace( _gl.CCW );
+ _gl.cullFace( scope.cullFace === THREE.CullFaceFront ? _gl.FRONT : _gl.BACK );
+ _state.setDepthTest( true );
+
+ // save the existing viewport so it can be restored later
+ _renderer.getViewport( _vector4 );
+
+ // render depth map
+
+ for ( var i = 0, il = _lights.length; i < il; i ++ ) {
+
+ var light = _lights[ i ];
+
+ if ( light.castShadow === true ) {
+
+ var shadow = light.shadow;
+ var shadowCamera = shadow.camera;
+ var shadowMapSize = shadow.mapSize;
+
+ if ( light instanceof THREE.PointLight ) {
+
+ faceCount = 6;
+ isPointLight = true;
+
+ var vpWidth = shadowMapSize.x / 4.0;
+ var vpHeight = shadowMapSize.y / 2.0;
+
+ // These viewports map a cube-map onto a 2D texture with the
+ // following orientation:
+ //
+ // xzXZ
+ // y Y
+ //
+ // X - Positive x direction
+ // x - Negative x direction
+ // Y - Positive y direction
+ // y - Negative y direction
+ // Z - Positive z direction
+ // z - Negative z direction
+
+ // positive X
+ cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight );
+ // negative X
+ cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight );
+ // positive Z
+ cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight );
+ // negative Z
+ cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight );
+ // positive Y
+ cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight );
+ // negative Y
+ cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight );
+
+ } else {
+
+ faceCount = 1;
+ isPointLight = false;
+
+ }
+
+ if ( shadow.map === null ) {
+
+ var shadowFilter = THREE.LinearFilter;
+
+ if ( scope.type === THREE.PCFSoftShadowMap ) {
+
+ shadowFilter = THREE.NearestFilter;
+
+ }
+
+ var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat };
+
+ shadow.map = new THREE.WebGLRenderTarget( shadowMapSize.x, shadowMapSize.y, pars );
+ shadow.matrix = new THREE.Matrix4();
+
+ //
+
+ if ( light instanceof THREE.SpotLight ) {
+
+ shadowCamera.aspect = shadowMapSize.x / shadowMapSize.y;
+
+ }
+
+ shadowCamera.updateProjectionMatrix();
+
+ }
+
+ var shadowMap = shadow.map;
+ var shadowMatrix = shadow.matrix;
+
+ _lightPositionWorld.setFromMatrixPosition( light.matrixWorld );
+ shadowCamera.position.copy( _lightPositionWorld );
+
+ _renderer.setRenderTarget( shadowMap );
+ _renderer.clear();
+
+ // render shadow map for each cube face (if omni-directional) or
+ // run a single pass if not
+
+ for ( var face = 0; face < faceCount; face ++ ) {
+
+ if ( isPointLight ) {
+
+ _lookTarget.copy( shadowCamera.position );
+ _lookTarget.add( cubeDirections[ face ] );
+ shadowCamera.up.copy( cubeUps[ face ] );
+ shadowCamera.lookAt( _lookTarget );
+ var vpDimensions = cube2DViewPorts[ face ];
+ _renderer.setViewport( vpDimensions.x, vpDimensions.y, vpDimensions.z, vpDimensions.w );
+
+ } else {
+
+ _lookTarget.setFromMatrixPosition( light.target.matrixWorld );
+ shadowCamera.lookAt( _lookTarget );
+
+ }
+
+ shadowCamera.updateMatrixWorld();
+ shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
+
+ // compute shadow matrix
+
+ shadowMatrix.set(
+ 0.5, 0.0, 0.0, 0.5,
+ 0.0, 0.5, 0.0, 0.5,
+ 0.0, 0.0, 0.5, 0.5,
+ 0.0, 0.0, 0.0, 1.0
+ );
+
+ shadowMatrix.multiply( shadowCamera.projectionMatrix );
+ shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
+
+ // update camera matrices and frustum
+
+ _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
+ _frustum.setFromMatrix( _projScreenMatrix );
+
+ // set object matrices & frustum culling
+
+ _renderList.length = 0;
+
+ projectObject( scene, shadowCamera );
+
+ // render shadow map
+ // render regular objects
+
+ for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) {
+
+ var object = _renderList[ j ];
+ var geometry = _objects.update( object );
+ var material = object.material;
+
+ if ( material instanceof THREE.MeshFaceMaterial ) {
+
+ var groups = geometry.groups;
+ var materials = material.materials;
+
+ for ( var k = 0, kl = groups.length; k < kl; k ++ ) {
+
+ var group = groups[ k ];
+ var groupMaterial = materials[ group.materialIndex ];
+
+ if ( groupMaterial.visible === true ) {
+
+ var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld );
+ _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, group );
+
+ }
+
+ }
+
+ } else {
+
+ var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld );
+ _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, null );
+
+ }
+
+ }
+
+ }
+
+ // We must call _renderer.resetGLState() at the end of each iteration of
+ // the light loop in order to force material updates for each light.
+ _renderer.resetGLState();
+
+ }
+
+ }
+
+ _renderer.setViewport( _vector4.x, _vector4.y, _vector4.z, _vector4.w );
+
+ // Restore GL state.
+ var clearColor = _renderer.getClearColor(),
+ clearAlpha = _renderer.getClearAlpha();
+ _renderer.setClearColor( clearColor, clearAlpha );
+ _state.enable( _gl.BLEND );
+
+ if ( scope.cullFace === THREE.CullFaceFront ) {
+
+ _gl.cullFace( _gl.BACK );
+
+ }
+
+ _renderer.resetGLState();
+
+ scope.needsUpdate = false;
+
+ };
+
+ function getDepthMaterial( object, material, isPointLight, lightPositionWorld ) {
+
+ var geometry = object.geometry;
+
+ var newMaterial = null;
+
+ var materialVariants = _depthMaterials;
+ var customMaterial = object.customDepthMaterial;
+
+ if ( isPointLight ) {
+
+ materialVariants = _distanceMaterials;
+ customMaterial = object.customDistanceMaterial;
+
+ }
+
+ if ( ! customMaterial ) {
+
+ var useMorphing = geometry.morphTargets !== undefined &&
+ geometry.morphTargets.length > 0 && material.morphTargets;
+
+ var useSkinning = object instanceof THREE.SkinnedMesh && material.skinning;
+
+ var variantIndex = 0;
+
+ if ( useMorphing ) variantIndex |= _MorphingFlag;
+ if ( useSkinning ) variantIndex |= _SkinningFlag;
+
+ newMaterial = materialVariants[ variantIndex ];
+
+ } else {
+
+ newMaterial = customMaterial;
+
+ }
+
+ newMaterial.visible = material.visible;
+ newMaterial.wireframe = material.wireframe;
+ newMaterial.wireframeLinewidth = material.wireframeLinewidth;
+
+ if ( isPointLight && newMaterial.uniforms.lightPos !== undefined ) {
+
+ newMaterial.uniforms.lightPos.value.copy( lightPositionWorld );
+
+ }
+
+ return newMaterial;
+
+ }
+
+ function projectObject( object, camera ) {
+
+ if ( object.visible === false ) return;
+
+ if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) {
+
+ if ( object.castShadow && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) {
+
+ var material = object.material;
+
+ if ( material.visible === true ) {
+
+ object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+ _renderList.push( object );
+
+ }
+
+ }
+
+ }
+
+ var children = object.children;
+
+ for ( var i = 0, l = children.length; i < l; i ++ ) {
+
+ projectObject( children[ i ], camera );
+
+ }
+
+ }
+
+};
+
+// File:src/renderers/webgl/WebGLState.js
+
+/**
+* @author mrdoob / http://mrdoob.com/
+*/
+
+THREE.WebGLState = function ( gl, extensions, paramThreeToGL ) {
+
+ var _this = this;
+
+ var newAttributes = new Uint8Array( 16 );
+ var enabledAttributes = new Uint8Array( 16 );
+ var attributeDivisors = new Uint8Array( 16 );
+
+ var capabilities = {};
+
+ var compressedTextureFormats = null;
+
+ var currentBlending = null;
+ var currentBlendEquation = null;
+ var currentBlendSrc = null;
+ var currentBlendDst = null;
+ var currentBlendEquationAlpha = null;
+ var currentBlendSrcAlpha = null;
+ var currentBlendDstAlpha = null;
+
+ var currentDepthFunc = null;
+ var currentDepthWrite = null;
+
+ var currentColorWrite = null;
+
+ var currentFlipSided = null;
+
+ var currentLineWidth = null;
+
+ var currentPolygonOffsetFactor = null;
+ var currentPolygonOffsetUnits = null;
+
+ var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );
+
+ var currentTextureSlot = undefined;
+ var currentBoundTextures = {};
+
+ this.init = function () {
+
+ gl.clearColor( 0, 0, 0, 1 );
+ gl.clearDepth( 1 );
+ gl.clearStencil( 0 );
+
+ this.enable( gl.DEPTH_TEST );
+ gl.depthFunc( gl.LEQUAL );
+
+ gl.frontFace( gl.CCW );
+ gl.cullFace( gl.BACK );
+ this.enable( gl.CULL_FACE );
+
+ this.enable( gl.BLEND );
+ gl.blendEquation( gl.FUNC_ADD );
+ gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );
+
+ };
+
+ this.initAttributes = function () {
+
+ for ( var i = 0, l = newAttributes.length; i < l; i ++ ) {
+
+ newAttributes[ i ] = 0;
+
+ }
+
+ };
+
+ this.enableAttribute = function ( attribute ) {
+
+ newAttributes[ attribute ] = 1;
+
+ if ( enabledAttributes[ attribute ] === 0 ) {
+
+ gl.enableVertexAttribArray( attribute );
+ enabledAttributes[ attribute ] = 1;
+
+ }
+
+ if ( attributeDivisors[ attribute ] !== 0 ) {
+
+ var extension = extensions.get( 'ANGLE_instanced_arrays' );
+
+ extension.vertexAttribDivisorANGLE( attribute, 0 );
+ attributeDivisors[ attribute ] = 0;
+
+ }
+
+ };
+
+ this.enableAttributeAndDivisor = function ( attribute, meshPerAttribute, extension ) {
+
+ newAttributes[ attribute ] = 1;
+
+ if ( enabledAttributes[ attribute ] === 0 ) {
+
+ gl.enableVertexAttribArray( attribute );
+ enabledAttributes[ attribute ] = 1;
+
+ }
+
+ if ( attributeDivisors[ attribute ] !== meshPerAttribute ) {
+
+ extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute );
+ attributeDivisors[ attribute ] = meshPerAttribute;
+
+ }
+
+ };
+
+ this.disableUnusedAttributes = function () {
+
+ for ( var i = 0, l = enabledAttributes.length; i < l; i ++ ) {
+
+ if ( enabledAttributes[ i ] !== newAttributes[ i ] ) {
+
+ gl.disableVertexAttribArray( i );
+ enabledAttributes[ i ] = 0;
+
+ }
+
+ }
+
+ };
+
+ this.enable = function ( id ) {
+
+ if ( capabilities[ id ] !== true ) {
+
+ gl.enable( id );
+ capabilities[ id ] = true;
+
+ }
+
+ };
+
+ this.disable = function ( id ) {
+
+ if ( capabilities[ id ] !== false ) {
+
+ gl.disable( id );
+ capabilities[ id ] = false;
+
+ }
+
+ };
+
+ this.getCompressedTextureFormats = function () {
+
+ if ( compressedTextureFormats === null ) {
+
+ compressedTextureFormats = [];
+
+ if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) ||
+ extensions.get( 'WEBGL_compressed_texture_s3tc' ) ) {
+
+ var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS );
+
+ for ( var i = 0; i < formats.length; i ++ ) {
+
+ compressedTextureFormats.push( formats[ i ] );
+
+ }
+
+ }
+
+ }
+
+ return compressedTextureFormats;
+
+ };
+
+ this.setBlending = function ( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha ) {
+
+ if ( blending !== currentBlending ) {
+
+ if ( blending === THREE.NoBlending ) {
+
+ this.disable( gl.BLEND );
+
+ } else if ( blending === THREE.AdditiveBlending ) {
+
+ this.enable( gl.BLEND );
+ gl.blendEquation( gl.FUNC_ADD );
+ gl.blendFunc( gl.SRC_ALPHA, gl.ONE );
+
+ } else if ( blending === THREE.SubtractiveBlending ) {
+
+ // TODO: Find blendFuncSeparate() combination
+
+ this.enable( gl.BLEND );
+ gl.blendEquation( gl.FUNC_ADD );
+ gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR );
+
+ } else if ( blending === THREE.MultiplyBlending ) {
+
+ // TODO: Find blendFuncSeparate() combination
+
+ this.enable( gl.BLEND );
+ gl.blendEquation( gl.FUNC_ADD );
+ gl.blendFunc( gl.ZERO, gl.SRC_COLOR );
+
+ } else if ( blending === THREE.CustomBlending ) {
+
+ this.enable( gl.BLEND );
+
+ } else {
+
+ this.enable( gl.BLEND );
+ gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
+ gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
+
+ }
+
+ currentBlending = blending;
+
+ }
+
+ if ( blending === THREE.CustomBlending ) {
+
+ blendEquationAlpha = blendEquationAlpha || blendEquation;
+ blendSrcAlpha = blendSrcAlpha || blendSrc;
+ blendDstAlpha = blendDstAlpha || blendDst;
+
+ if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {
+
+ gl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) );
+
+ currentBlendEquation = blendEquation;
+ currentBlendEquationAlpha = blendEquationAlpha;
+
+ }
+
+ if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {
+
+ gl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) );
+
+ currentBlendSrc = blendSrc;
+ currentBlendDst = blendDst;
+ currentBlendSrcAlpha = blendSrcAlpha;
+ currentBlendDstAlpha = blendDstAlpha;
+
+ }
+
+ } else {
+
+ currentBlendEquation = null;
+ currentBlendSrc = null;
+ currentBlendDst = null;
+ currentBlendEquationAlpha = null;
+ currentBlendSrcAlpha = null;
+ currentBlendDstAlpha = null;
+
+ }
+
+ };
+
+ this.setDepthFunc = function ( depthFunc ) {
+
+ if ( currentDepthFunc !== depthFunc ) {
+
+ if ( depthFunc ) {
+
+ switch ( depthFunc ) {
+
+ case THREE.NeverDepth:
+
+ gl.depthFunc( gl.NEVER );
+ break;
+
+ case THREE.AlwaysDepth:
+
+ gl.depthFunc( gl.ALWAYS );
+ break;
+
+ case THREE.LessDepth:
+
+ gl.depthFunc( gl.LESS );
+ break;
+
+ case THREE.LessEqualDepth:
+
+ gl.depthFunc( gl.LEQUAL );
+ break;
+
+ case THREE.EqualDepth:
+
+ gl.depthFunc( gl.EQUAL );
+ break;
+
+ case THREE.GreaterEqualDepth:
+
+ gl.depthFunc( gl.GEQUAL );
+ break;
+
+ case THREE.GreaterDepth:
+
+ gl.depthFunc( gl.GREATER );
+ break;
+
+ case THREE.NotEqualDepth:
+
+ gl.depthFunc( gl.NOTEQUAL );
+ break;
+
+ default:
+
+ gl.depthFunc( gl.LEQUAL );
+
+ }
+
+ } else {
+
+ gl.depthFunc( gl.LEQUAL );
+
+ }
+
+ currentDepthFunc = depthFunc;
+
+ }
+
+ };
+
+ this.setDepthTest = function ( depthTest ) {
+
+ if ( depthTest ) {
+
+ this.enable( gl.DEPTH_TEST );
+
+ } else {
+
+ this.disable( gl.DEPTH_TEST );
+
+ }
+
+ };
+
+ this.setDepthWrite = function ( depthWrite ) {
+
+ if ( currentDepthWrite !== depthWrite ) {
+
+ gl.depthMask( depthWrite );
+ currentDepthWrite = depthWrite;
+
+ }
+
+ };
+
+ this.setColorWrite = function ( colorWrite ) {
+
+ if ( currentColorWrite !== colorWrite ) {
+
+ gl.colorMask( colorWrite, colorWrite, colorWrite, colorWrite );
+ currentColorWrite = colorWrite;
+
+ }
+
+ };
+
+ this.setFlipSided = function ( flipSided ) {
+
+ if ( currentFlipSided !== flipSided ) {
+
+ if ( flipSided ) {
+
+ gl.frontFace( gl.CW );
+
+ } else {
+
+ gl.frontFace( gl.CCW );
+
+ }
+
+ currentFlipSided = flipSided;
+
+ }
+
+ };
+
+ this.setLineWidth = function ( width ) {
+
+ if ( width !== currentLineWidth ) {
+
+ gl.lineWidth( width );
+
+ currentLineWidth = width;
+
+ }
+
+ };
+
+ this.setPolygonOffset = function ( polygonOffset, factor, units ) {
+
+ if ( polygonOffset ) {
+
+ this.enable( gl.POLYGON_OFFSET_FILL );
+
+ } else {
+
+ this.disable( gl.POLYGON_OFFSET_FILL );
+
+ }
+
+ if ( polygonOffset && ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) ) {
+
+ gl.polygonOffset( factor, units );
+
+ currentPolygonOffsetFactor = factor;
+ currentPolygonOffsetUnits = units;
+
+ }
+
+ };
+
+ this.setScissorTest = function ( scissorTest ) {
+
+ if ( scissorTest ) {
+
+ this.enable( gl.SCISSOR_TEST );
+
+ } else {
+
+ this.disable( gl.SCISSOR_TEST );
+
+ }
+
+ };
+
+ // texture
+
+ this.activeTexture = function ( webglSlot ) {
+
+ if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;
+
+ if ( currentTextureSlot !== webglSlot ) {
+
+ gl.activeTexture( webglSlot );
+ currentTextureSlot = webglSlot;
+
+ }
+
+ }
+
+ this.bindTexture = function ( webglType, webglTexture ) {
+
+ if ( currentTextureSlot === undefined ) {
+
+ _this.activeTexture();
+
+ }
+
+ var boundTexture = currentBoundTextures[ currentTextureSlot ];
+
+ if ( boundTexture === undefined ) {
+
+ boundTexture = { type: undefined, texture: undefined };
+ currentBoundTextures[ currentTextureSlot ] = boundTexture;
+
+ }
+
+ if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {
+
+ gl.bindTexture( webglType, webglTexture );
+
+ boundTexture.type = webglType;
+ boundTexture.texture = webglTexture;
+
+ }
+
+ };
+
+ this.compressedTexImage2D = function () {
+
+ try {
+
+ gl.compressedTexImage2D.apply( gl, arguments );
+
+ } catch ( error ) {
+
+ console.error( error );
+
+ }
+
+ };
+
+ this.texImage2D = function () {
+
+ try {
+
+ gl.texImage2D.apply( gl, arguments );
+
+ } catch ( error ) {
+
+ console.error( error );
+
+ }
+
+ };
+
+ //
+
+ this.reset = function () {
+
+ for ( var i = 0; i < enabledAttributes.length; i ++ ) {
+
+ if ( enabledAttributes[ i ] === 1 ) {
+
+ gl.disableVertexAttribArray( i );
+ enabledAttributes[ i ] = 0;
+
+ }
+
+ }
+
+ capabilities = {};
+
+ compressedTextureFormats = null;
+
+ currentBlending = null;
+
+ currentDepthWrite = null;
+ currentColorWrite = null;
+
+ currentFlipSided = null;
+
+ };
+
+};
+
+// File:src/renderers/webgl/plugins/LensFlarePlugin.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.LensFlarePlugin = function ( renderer, flares ) {
+
+ var gl = renderer.context;
+ var state = renderer.state;
+
+ var vertexBuffer, elementBuffer;
+ var program, attributes, uniforms;
+ var hasVertexTexture;
+
+ var tempTexture, occlusionTexture;
+
+ function init() {
+
+ var vertices = new Float32Array( [
+ - 1, - 1, 0, 0,
+ 1, - 1, 1, 0,
+ 1, 1, 1, 1,
+ - 1, 1, 0, 1
+ ] );
+
+ var faces = new Uint16Array( [
+ 0, 1, 2,
+ 0, 2, 3
+ ] );
+
+ // buffers
+
+ vertexBuffer = gl.createBuffer();
+ elementBuffer = gl.createBuffer();
+
+ gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
+ gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
+
+ gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
+ gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
+
+ // textures
+
+ tempTexture = gl.createTexture();
+ occlusionTexture = gl.createTexture();
+
+ state.bindTexture( gl.TEXTURE_2D, tempTexture );
+ gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
+
+ state.bindTexture( gl.TEXTURE_2D, occlusionTexture );
+ gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
+
+ hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0;
+
+ var shader;
+
+ if ( hasVertexTexture ) {
+
+ shader = {
+
+ vertexShader: [
+
+ "uniform lowp int renderType;",
+
+ "uniform vec3 screenPosition;",
+ "uniform vec2 scale;",
+ "uniform float rotation;",
+
+ "uniform sampler2D occlusionMap;",
+
+ "attribute vec2 position;",
+ "attribute vec2 uv;",
+
+ "varying vec2 vUV;",
+ "varying float vVisibility;",
+
+ "void main() {",
+
+ "vUV = uv;",
+
+ "vec2 pos = position;",
+
+ "if ( renderType == 2 ) {",
+
+ "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );",
+
+ "vVisibility = visibility.r / 9.0;",
+ "vVisibility *= 1.0 - visibility.g / 9.0;",
+ "vVisibility *= visibility.b / 9.0;",
+ "vVisibility *= 1.0 - visibility.a / 9.0;",
+
+ "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
+ "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
+
+ "}",
+
+ "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform lowp int renderType;",
+
+ "uniform sampler2D map;",
+ "uniform float opacity;",
+ "uniform vec3 color;",
+
+ "varying vec2 vUV;",
+ "varying float vVisibility;",
+
+ "void main() {",
+
+ // pink square
+
+ "if ( renderType == 0 ) {",
+
+ "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
+
+ // restore
+
+ "} else if ( renderType == 1 ) {",
+
+ "gl_FragColor = texture2D( map, vUV );",
+
+ // flare
+
+ "} else {",
+
+ "vec4 texture = texture2D( map, vUV );",
+ "texture.a *= opacity * vVisibility;",
+ "gl_FragColor = texture;",
+ "gl_FragColor.rgb *= color;",
+
+ "}",
+
+ "}"
+
+ ].join( "\n" )
+
+ };
+
+ } else {
+
+ shader = {
+
+ vertexShader: [
+
+ "uniform lowp int renderType;",
+
+ "uniform vec3 screenPosition;",
+ "uniform vec2 scale;",
+ "uniform float rotation;",
+
+ "attribute vec2 position;",
+ "attribute vec2 uv;",
+
+ "varying vec2 vUV;",
+
+ "void main() {",
+
+ "vUV = uv;",
+
+ "vec2 pos = position;",
+
+ "if ( renderType == 2 ) {",
+
+ "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
+ "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
+
+ "}",
+
+ "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "precision mediump float;",
+
+ "uniform lowp int renderType;",
+
+ "uniform sampler2D map;",
+ "uniform sampler2D occlusionMap;",
+ "uniform float opacity;",
+ "uniform vec3 color;",
+
+ "varying vec2 vUV;",
+
+ "void main() {",
+
+ // pink square
+
+ "if ( renderType == 0 ) {",
+
+ "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );",
+
+ // restore
+
+ "} else if ( renderType == 1 ) {",
+
+ "gl_FragColor = texture2D( map, vUV );",
+
+ // flare
+
+ "} else {",
+
+ "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;",
+ "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;",
+ "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;",
+ "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;",
+ "visibility = ( 1.0 - visibility / 4.0 );",
+
+ "vec4 texture = texture2D( map, vUV );",
+ "texture.a *= opacity * visibility;",
+ "gl_FragColor = texture;",
+ "gl_FragColor.rgb *= color;",
+
+ "}",
+
+ "}"
+
+ ].join( "\n" )
+
+ };
+
+ }
+
+ program = createProgram( shader );
+
+ attributes = {
+ vertex: gl.getAttribLocation ( program, "position" ),
+ uv: gl.getAttribLocation ( program, "uv" )
+ };
+
+ uniforms = {
+ renderType: gl.getUniformLocation( program, "renderType" ),
+ map: gl.getUniformLocation( program, "map" ),
+ occlusionMap: gl.getUniformLocation( program, "occlusionMap" ),
+ opacity: gl.getUniformLocation( program, "opacity" ),
+ color: gl.getUniformLocation( program, "color" ),
+ scale: gl.getUniformLocation( program, "scale" ),
+ rotation: gl.getUniformLocation( program, "rotation" ),
+ screenPosition: gl.getUniformLocation( program, "screenPosition" )
+ };
+
+ }
+
+ /*
+ * Render lens flares
+ * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
+ * reads these back and calculates occlusion.
+ */
+
+ this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
+
+ if ( flares.length === 0 ) return;
+
+ var tempPosition = new THREE.Vector3();
+
+ var invAspect = viewportHeight / viewportWidth,
+ halfViewportWidth = viewportWidth * 0.5,
+ halfViewportHeight = viewportHeight * 0.5;
+
+ var size = 16 / viewportHeight,
+ scale = new THREE.Vector2( size * invAspect, size );
+
+ var screenPosition = new THREE.Vector3( 1, 1, 0 ),
+ screenPositionPixels = new THREE.Vector2( 1, 1 );
+
+ if ( program === undefined ) {
+
+ init();
+
+ }
+
+ gl.useProgram( program );
+
+ state.initAttributes();
+ state.enableAttribute( attributes.vertex );
+ state.enableAttribute( attributes.uv );
+ state.disableUnusedAttributes();
+
+ // loop through all lens flares to update their occlusion and positions
+ // setup gl and common used attribs/uniforms
+
+ gl.uniform1i( uniforms.occlusionMap, 0 );
+ gl.uniform1i( uniforms.map, 1 );
+
+ gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
+ gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 );
+ gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );
+
+ gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
+
+ state.disable( gl.CULL_FACE );
+ gl.depthMask( false );
+
+ for ( var i = 0, l = flares.length; i < l; i ++ ) {
+
+ size = 16 / viewportHeight;
+ scale.set( size * invAspect, size );
+
+ // calc object screen position
+
+ var flare = flares[ i ];
+
+ tempPosition.set( flare.matrixWorld.elements[ 12 ], flare.matrixWorld.elements[ 13 ], flare.matrixWorld.elements[ 14 ] );
+
+ tempPosition.applyMatrix4( camera.matrixWorldInverse );
+ tempPosition.applyProjection( camera.projectionMatrix );
+
+ // setup arrays for gl programs
+
+ screenPosition.copy( tempPosition );
+
+ screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth;
+ screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight;
+
+ // screen cull
+
+ if ( hasVertexTexture || (
+ screenPositionPixels.x > 0 &&
+ screenPositionPixels.x < viewportWidth &&
+ screenPositionPixels.y > 0 &&
+ screenPositionPixels.y < viewportHeight ) ) {
+
+ // save current RGB to temp texture
+
+ state.activeTexture( gl.TEXTURE0 );
+ state.bindTexture( gl.TEXTURE_2D, null );
+ state.activeTexture( gl.TEXTURE1 );
+ state.bindTexture( gl.TEXTURE_2D, tempTexture );
+ gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
+
+
+ // render pink quad
+
+ gl.uniform1i( uniforms.renderType, 0 );
+ gl.uniform2f( uniforms.scale, scale.x, scale.y );
+ gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
+
+ state.disable( gl.BLEND );
+ state.enable( gl.DEPTH_TEST );
+
+ gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
+
+
+ // copy result to occlusionMap
+
+ state.activeTexture( gl.TEXTURE0 );
+ state.bindTexture( gl.TEXTURE_2D, occlusionTexture );
+ gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
+
+
+ // restore graphics
+
+ gl.uniform1i( uniforms.renderType, 1 );
+ state.disable( gl.DEPTH_TEST );
+
+ state.activeTexture( gl.TEXTURE1 );
+ state.bindTexture( gl.TEXTURE_2D, tempTexture );
+ gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
+
+
+ // update object positions
+
+ flare.positionScreen.copy( screenPosition );
+
+ if ( flare.customUpdateCallback ) {
+
+ flare.customUpdateCallback( flare );
+
+ } else {
+
+ flare.updateLensFlares();
+
+ }
+
+ // render flares
+
+ gl.uniform1i( uniforms.renderType, 2 );
+ state.enable( gl.BLEND );
+
+ for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) {
+
+ var sprite = flare.lensFlares[ j ];
+
+ if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) {
+
+ screenPosition.x = sprite.x;
+ screenPosition.y = sprite.y;
+ screenPosition.z = sprite.z;
+
+ size = sprite.size * sprite.scale / viewportHeight;
+
+ scale.x = size * invAspect;
+ scale.y = size;
+
+ gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
+ gl.uniform2f( uniforms.scale, scale.x, scale.y );
+ gl.uniform1f( uniforms.rotation, sprite.rotation );
+
+ gl.uniform1f( uniforms.opacity, sprite.opacity );
+ gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b );
+
+ state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst );
+ renderer.setTexture( sprite.texture, 1 );
+
+ gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ // restore gl
+
+ state.enable( gl.CULL_FACE );
+ state.enable( gl.DEPTH_TEST );
+ gl.depthMask( true );
+
+ renderer.resetGLState();
+
+ };
+
+ function createProgram ( shader ) {
+
+ var program = gl.createProgram();
+
+ var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
+ var vertexShader = gl.createShader( gl.VERTEX_SHADER );
+
+ var prefix = "precision " + renderer.getPrecision() + " float;\n";
+
+ gl.shaderSource( fragmentShader, prefix + shader.fragmentShader );
+ gl.shaderSource( vertexShader, prefix + shader.vertexShader );
+
+ gl.compileShader( fragmentShader );
+ gl.compileShader( vertexShader );
+
+ gl.attachShader( program, fragmentShader );
+ gl.attachShader( program, vertexShader );
+
+ gl.linkProgram( program );
+
+ return program;
+
+ }
+
+};
+
+// File:src/renderers/webgl/plugins/SpritePlugin.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SpritePlugin = function ( renderer, sprites ) {
+
+ var gl = renderer.context;
+ var state = renderer.state;
+
+ var vertexBuffer, elementBuffer;
+ var program, attributes, uniforms;
+
+ var texture;
+
+ // decompose matrixWorld
+
+ var spritePosition = new THREE.Vector3();
+ var spriteRotation = new THREE.Quaternion();
+ var spriteScale = new THREE.Vector3();
+
+ function init() {
+
+ var vertices = new Float32Array( [
+ - 0.5, - 0.5, 0, 0,
+ 0.5, - 0.5, 1, 0,
+ 0.5, 0.5, 1, 1,
+ - 0.5, 0.5, 0, 1
+ ] );
+
+ var faces = new Uint16Array( [
+ 0, 1, 2,
+ 0, 2, 3
+ ] );
+
+ vertexBuffer = gl.createBuffer();
+ elementBuffer = gl.createBuffer();
+
+ gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
+ gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
+
+ gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
+ gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
+
+ program = createProgram();
+
+ attributes = {
+ position: gl.getAttribLocation ( program, 'position' ),
+ uv: gl.getAttribLocation ( program, 'uv' )
+ };
+
+ uniforms = {
+ uvOffset: gl.getUniformLocation( program, 'uvOffset' ),
+ uvScale: gl.getUniformLocation( program, 'uvScale' ),
+
+ rotation: gl.getUniformLocation( program, 'rotation' ),
+ scale: gl.getUniformLocation( program, 'scale' ),
+
+ color: gl.getUniformLocation( program, 'color' ),
+ map: gl.getUniformLocation( program, 'map' ),
+ opacity: gl.getUniformLocation( program, 'opacity' ),
+
+ modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ),
+ projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ),
+
+ fogType: gl.getUniformLocation( program, 'fogType' ),
+ fogDensity: gl.getUniformLocation( program, 'fogDensity' ),
+ fogNear: gl.getUniformLocation( program, 'fogNear' ),
+ fogFar: gl.getUniformLocation( program, 'fogFar' ),
+ fogColor: gl.getUniformLocation( program, 'fogColor' ),
+
+ alphaTest: gl.getUniformLocation( program, 'alphaTest' )
+ };
+
+ var canvas = document.createElement( 'canvas' );
+ canvas.width = 8;
+ canvas.height = 8;
+
+ var context = canvas.getContext( '2d' );
+ context.fillStyle = 'white';
+ context.fillRect( 0, 0, 8, 8 );
+
+ texture = new THREE.Texture( canvas );
+ texture.needsUpdate = true;
+
+ }
+
+ this.render = function ( scene, camera ) {
+
+ if ( sprites.length === 0 ) return;
+
+ // setup gl
+
+ if ( program === undefined ) {
+
+ init();
+
+ }
+
+ gl.useProgram( program );
+
+ state.initAttributes();
+ state.enableAttribute( attributes.position );
+ state.enableAttribute( attributes.uv );
+ state.disableUnusedAttributes();
+
+ state.disable( gl.CULL_FACE );
+ state.enable( gl.BLEND );
+
+ gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
+ gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 );
+ gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );
+
+ gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
+
+ gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
+
+ state.activeTexture( gl.TEXTURE0 );
+ gl.uniform1i( uniforms.map, 0 );
+
+ var oldFogType = 0;
+ var sceneFogType = 0;
+ var fog = scene.fog;
+
+ if ( fog ) {
+
+ gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b );
+
+ if ( fog instanceof THREE.Fog ) {
+
+ gl.uniform1f( uniforms.fogNear, fog.near );
+ gl.uniform1f( uniforms.fogFar, fog.far );
+
+ gl.uniform1i( uniforms.fogType, 1 );
+ oldFogType = 1;
+ sceneFogType = 1;
+
+ } else if ( fog instanceof THREE.FogExp2 ) {
+
+ gl.uniform1f( uniforms.fogDensity, fog.density );
+
+ gl.uniform1i( uniforms.fogType, 2 );
+ oldFogType = 2;
+ sceneFogType = 2;
+
+ }
+
+ } else {
+
+ gl.uniform1i( uniforms.fogType, 0 );
+ oldFogType = 0;
+ sceneFogType = 0;
+
+ }
+
+
+ // update positions and sort
+
+ for ( var i = 0, l = sprites.length; i < l; i ++ ) {
+
+ var sprite = sprites[ i ];
+
+ sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );
+ sprite.z = - sprite.modelViewMatrix.elements[ 14 ];
+
+ }
+
+ sprites.sort( painterSortStable );
+
+ // render all sprites
+
+ var scale = [];
+
+ for ( var i = 0, l = sprites.length; i < l; i ++ ) {
+
+ var sprite = sprites[ i ];
+ var material = sprite.material;
+
+ gl.uniform1f( uniforms.alphaTest, material.alphaTest );
+ gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements );
+
+ sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale );
+
+ scale[ 0 ] = spriteScale.x;
+ scale[ 1 ] = spriteScale.y;
+
+ var fogType = 0;
+
+ if ( scene.fog && material.fog ) {
+
+ fogType = sceneFogType;
+
+ }
+
+ if ( oldFogType !== fogType ) {
+
+ gl.uniform1i( uniforms.fogType, fogType );
+ oldFogType = fogType;
+
+ }
+
+ if ( material.map !== null ) {
+
+ gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y );
+ gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y );
+
+ } else {
+
+ gl.uniform2f( uniforms.uvOffset, 0, 0 );
+ gl.uniform2f( uniforms.uvScale, 1, 1 );
+
+ }
+
+ gl.uniform1f( uniforms.opacity, material.opacity );
+ gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b );
+
+ gl.uniform1f( uniforms.rotation, material.rotation );
+ gl.uniform2fv( uniforms.scale, scale );
+
+ state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
+ state.setDepthTest( material.depthTest );
+ state.setDepthWrite( material.depthWrite );
+
+ if ( material.map && material.map.image && material.map.image.width ) {
+
+ renderer.setTexture( material.map, 0 );
+
+ } else {
+
+ renderer.setTexture( texture, 0 );
+
+ }
+
+ gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
+
+ }
+
+ // restore gl
+
+ state.enable( gl.CULL_FACE );
+
+ renderer.resetGLState();
+
+ };
+
+ function createProgram () {
+
+ var program = gl.createProgram();
+
+ var vertexShader = gl.createShader( gl.VERTEX_SHADER );
+ var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
+
+ gl.shaderSource( vertexShader, [
+
+ 'precision ' + renderer.getPrecision() + ' float;',
+
+ 'uniform mat4 modelViewMatrix;',
+ 'uniform mat4 projectionMatrix;',
+ 'uniform float rotation;',
+ 'uniform vec2 scale;',
+ 'uniform vec2 uvOffset;',
+ 'uniform vec2 uvScale;',
+
+ 'attribute vec2 position;',
+ 'attribute vec2 uv;',
+
+ 'varying vec2 vUV;',
+
+ 'void main() {',
+
+ 'vUV = uvOffset + uv * uvScale;',
+
+ 'vec2 alignedPosition = position * scale;',
+
+ 'vec2 rotatedPosition;',
+ 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;',
+ 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;',
+
+ 'vec4 finalPosition;',
+
+ 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',
+ 'finalPosition.xy += rotatedPosition;',
+ 'finalPosition = projectionMatrix * finalPosition;',
+
+ 'gl_Position = finalPosition;',
+
+ '}'
+
+ ].join( '\n' ) );
+
+ gl.shaderSource( fragmentShader, [
+
+ 'precision ' + renderer.getPrecision() + ' float;',
+
+ 'uniform vec3 color;',
+ 'uniform sampler2D map;',
+ 'uniform float opacity;',
+
+ 'uniform int fogType;',
+ 'uniform vec3 fogColor;',
+ 'uniform float fogDensity;',
+ 'uniform float fogNear;',
+ 'uniform float fogFar;',
+ 'uniform float alphaTest;',
+
+ 'varying vec2 vUV;',
+
+ 'void main() {',
+
+ 'vec4 texture = texture2D( map, vUV );',
+
+ 'if ( texture.a < alphaTest ) discard;',
+
+ 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );',
+
+ 'if ( fogType > 0 ) {',
+
+ 'float depth = gl_FragCoord.z / gl_FragCoord.w;',
+ 'float fogFactor = 0.0;',
+
+ 'if ( fogType == 1 ) {',
+
+ 'fogFactor = smoothstep( fogNear, fogFar, depth );',
+
+ '} else {',
+
+ 'const float LOG2 = 1.442695;',
+ 'fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );',
+ 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );',
+
+ '}',
+
+ 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );',
+
+ '}',
+
+ '}'
+
+ ].join( '\n' ) );
+
+ gl.compileShader( vertexShader );
+ gl.compileShader( fragmentShader );
+
+ gl.attachShader( program, vertexShader );
+ gl.attachShader( program, fragmentShader );
+
+ gl.linkProgram( program );
+
+ return program;
+
+ }
+
+ function painterSortStable ( a, b ) {
+
+ if ( a.z !== b.z ) {
+
+ return b.z - a.z;
+
+ } else {
+
+ return b.id - a.id;
+
+ }
+
+ }
+
+};
+
+// File:src/extras/CurveUtils.js
+
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ */
+
+THREE.CurveUtils = {
+
+ tangentQuadraticBezier: function ( t, p0, p1, p2 ) {
+
+ return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
+
+ },
+
+ // Puay Bing, thanks for helping with this derivative!
+
+ tangentCubicBezier: function ( t, p0, p1, p2, p3 ) {
+
+ return - 3 * p0 * ( 1 - t ) * ( 1 - t ) +
+ 3 * p1 * ( 1 - t ) * ( 1 - t ) - 6 * t * p1 * ( 1 - t ) +
+ 6 * t * p2 * ( 1 - t ) - 3 * t * t * p2 +
+ 3 * t * t * p3;
+
+ },
+
+ tangentSpline: function ( t, p0, p1, p2, p3 ) {
+
+ // To check if my formulas are correct
+
+ var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1
+ var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t
+ var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2
+ var h11 = 3 * t * t - 2 * t; // t3 − t2
+
+ return h00 + h10 + h01 + h11;
+
+ },
+
+ // Catmull-Rom
+
+ interpolate: function( p0, p1, p2, p3, t ) {
+
+ var v0 = ( p2 - p0 ) * 0.5;
+ var v1 = ( p3 - p1 ) * 0.5;
+ var t2 = t * t;
+ var t3 = t * t2;
+ return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
+
+ }
+
+};
+
+// File:src/extras/GeometryUtils.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.GeometryUtils = {
+
+ merge: function ( geometry1, geometry2, materialIndexOffset ) {
+
+ console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' );
+
+ var matrix;
+
+ if ( geometry2 instanceof THREE.Mesh ) {
+
+ geometry2.matrixAutoUpdate && geometry2.updateMatrix();
+
+ matrix = geometry2.matrix;
+ geometry2 = geometry2.geometry;
+
+ }
+
+ geometry1.merge( geometry2, matrix, materialIndexOffset );
+
+ },
+
+ center: function ( geometry ) {
+
+ console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' );
+ return geometry.center();
+
+ }
+
+};
+
+// File:src/extras/ImageUtils.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author Daosheng Mu / https://github.com/DaoshengMu/
+ */
+
+THREE.ImageUtils = {
+
+ crossOrigin: undefined,
+
+ loadTexture: function ( url, mapping, onLoad, onError ) {
+
+ console.warn( 'THREE.ImageUtils.loadTexture is being deprecated. Use THREE.TextureLoader() instead.' );
+
+ var loader = new THREE.TextureLoader();
+ loader.setCrossOrigin( this.crossOrigin );
+
+ var texture = loader.load( url, onLoad, undefined, onError );
+
+ if ( mapping ) texture.mapping = mapping;
+
+ return texture;
+
+ },
+
+ loadTextureCube: function ( urls, mapping, onLoad, onError ) {
+
+ console.warn( 'THREE.ImageUtils.loadTextureCube is being deprecated. Use THREE.CubeTextureLoader() instead.' );
+
+ var loader = new THREE.CubeTextureLoader();
+ loader.setCrossOrigin( this.crossOrigin );
+
+ var texture = loader.load( urls, onLoad, undefined, onError );
+
+ if ( mapping ) texture.mapping = mapping;
+
+ return texture;
+
+ },
+
+ loadCompressedTexture: function () {
+
+ console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' )
+
+ },
+
+ loadCompressedTextureCube: function () {
+
+ console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' )
+
+ }
+
+};
+
+// File:src/extras/SceneUtils.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SceneUtils = {
+
+ createMultiMaterialObject: function ( geometry, materials ) {
+
+ var group = new THREE.Group();
+
+ for ( var i = 0, l = materials.length; i < l; i ++ ) {
+
+ group.add( new THREE.Mesh( geometry, materials[ i ] ) );
+
+ }
+
+ return group;
+
+ },
+
+ detach: function ( child, parent, scene ) {
+
+ child.applyMatrix( parent.matrixWorld );
+ parent.remove( child );
+ scene.add( child );
+
+ },
+
+ attach: function ( child, scene, parent ) {
+
+ var matrixWorldInverse = new THREE.Matrix4();
+ matrixWorldInverse.getInverse( parent.matrixWorld );
+ child.applyMatrix( matrixWorldInverse );
+
+ scene.remove( child );
+ parent.add( child );
+
+ }
+
+};
+
+// File:src/extras/ShapeUtils.js
+
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ */
+
+THREE.ShapeUtils = {
+
+ // calculate area of the contour polygon
+
+ area: function ( contour ) {
+
+ var n = contour.length;
+ var a = 0.0;
+
+ for ( var p = n - 1, q = 0; q < n; p = q ++ ) {
+
+ a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
+
+ }
+
+ return a * 0.5;
+
+ },
+
+ triangulate: ( function () {
+
+ /**
+ * This code is a quick port of code written in C++ which was submitted to
+ * flipcode.com by John W. Ratcliff // July 22, 2000
+ * See original code and more information here:
+ * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
+ *
+ * ported to actionscript by Zevan Rosser
+ * www.actionsnippet.com
+ *
+ * ported to javascript by Joshua Koo
+ * http://www.lab4games.net/zz85/blog
+ *
+ */
+
+ function snip( contour, u, v, w, n, verts ) {
+
+ var p;
+ var ax, ay, bx, by;
+ var cx, cy, px, py;
+
+ ax = contour[ verts[ u ] ].x;
+ ay = contour[ verts[ u ] ].y;
+
+ bx = contour[ verts[ v ] ].x;
+ by = contour[ verts[ v ] ].y;
+
+ cx = contour[ verts[ w ] ].x;
+ cy = contour[ verts[ w ] ].y;
+
+ if ( Number.EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false;
+
+ var aX, aY, bX, bY, cX, cY;
+ var apx, apy, bpx, bpy, cpx, cpy;
+ var cCROSSap, bCROSScp, aCROSSbp;
+
+ aX = cx - bx; aY = cy - by;
+ bX = ax - cx; bY = ay - cy;
+ cX = bx - ax; cY = by - ay;
+
+ for ( p = 0; p < n; p ++ ) {
+
+ px = contour[ verts[ p ] ].x;
+ py = contour[ verts[ p ] ].y;
+
+ if ( ( ( px === ax ) && ( py === ay ) ) ||
+ ( ( px === bx ) && ( py === by ) ) ||
+ ( ( px === cx ) && ( py === cy ) ) ) continue;
+
+ apx = px - ax; apy = py - ay;
+ bpx = px - bx; bpy = py - by;
+ cpx = px - cx; cpy = py - cy;
+
+ // see if p is inside triangle abc
+
+ aCROSSbp = aX * bpy - aY * bpx;
+ cCROSSap = cX * apy - cY * apx;
+ bCROSScp = bX * cpy - bY * cpx;
+
+ if ( ( aCROSSbp >= - Number.EPSILON ) && ( bCROSScp >= - Number.EPSILON ) && ( cCROSSap >= - Number.EPSILON ) ) return false;
+
+ }
+
+ return true;
+
+ }
+
+ // takes in an contour array and returns
+
+ return function ( contour, indices ) {
+
+ var n = contour.length;
+
+ if ( n < 3 ) return null;
+
+ var result = [],
+ verts = [],
+ vertIndices = [];
+
+ /* we want a counter-clockwise polygon in verts */
+
+ var u, v, w;
+
+ if ( THREE.ShapeUtils.area( contour ) > 0.0 ) {
+
+ for ( v = 0; v < n; v ++ ) verts[ v ] = v;
+
+ } else {
+
+ for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v;
+
+ }
+
+ var nv = n;
+
+ /* remove nv - 2 vertices, creating 1 triangle every time */
+
+ var count = 2 * nv; /* error detection */
+
+ for ( v = nv - 1; nv > 2; ) {
+
+ /* if we loop, it is probably a non-simple polygon */
+
+ if ( ( count -- ) <= 0 ) {
+
+ //** Triangulate: ERROR - probable bad polygon!
+
+ //throw ( "Warning, unable to triangulate polygon!" );
+ //return null;
+ // Sometimes warning is fine, especially polygons are triangulated in reverse.
+ console.warn( 'THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()' );
+
+ if ( indices ) return vertIndices;
+ return result;
+
+ }
+
+ /* three consecutive vertices in current polygon, */
+
+ u = v; if ( nv <= u ) u = 0; /* previous */
+ v = u + 1; if ( nv <= v ) v = 0; /* new v */
+ w = v + 1; if ( nv <= w ) w = 0; /* next */
+
+ if ( snip( contour, u, v, w, nv, verts ) ) {
+
+ var a, b, c, s, t;
+
+ /* true names of the vertices */
+
+ a = verts[ u ];
+ b = verts[ v ];
+ c = verts[ w ];
+
+ /* output Triangle */
+
+ result.push( [ contour[ a ],
+ contour[ b ],
+ contour[ c ] ] );
+
+
+ vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
+
+ /* remove v from the remaining polygon */
+
+ for ( s = v, t = v + 1; t < nv; s ++, t ++ ) {
+
+ verts[ s ] = verts[ t ];
+
+ }
+
+ nv --;
+
+ /* reset error detection counter */
+
+ count = 2 * nv;
+
+ }
+
+ }
+
+ if ( indices ) return vertIndices;
+ return result;
+
+ }
+
+ } )(),
+
+ triangulateShape: function ( contour, holes ) {
+
+ function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) {
+
+ // inOtherPt needs to be collinear to the inSegment
+ if ( inSegPt1.x !== inSegPt2.x ) {
+
+ if ( inSegPt1.x < inSegPt2.x ) {
+
+ return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) );
+
+ } else {
+
+ return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) );
+
+ }
+
+ } else {
+
+ if ( inSegPt1.y < inSegPt2.y ) {
+
+ return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) );
+
+ } else {
+
+ return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) );
+
+ }
+
+ }
+
+ }
+
+ function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) {
+
+ var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y;
+ var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y;
+
+ var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x;
+ var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y;
+
+ var limit = seg1dy * seg2dx - seg1dx * seg2dy;
+ var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy;
+
+ if ( Math.abs( limit ) > Number.EPSILON ) {
+
+ // not parallel
+
+ var perpSeg2;
+ if ( limit > 0 ) {
+
+ if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return [];
+ perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
+ if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return [];
+
+ } else {
+
+ if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return [];
+ perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
+ if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return [];
+
+ }
+
+ // i.e. to reduce rounding errors
+ // intersection at endpoint of segment#1?
+ if ( perpSeg2 === 0 ) {
+
+ if ( ( inExcludeAdjacentSegs ) &&
+ ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return [];
+ return [ inSeg1Pt1 ];
+
+ }
+ if ( perpSeg2 === limit ) {
+
+ if ( ( inExcludeAdjacentSegs ) &&
+ ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return [];
+ return [ inSeg1Pt2 ];
+
+ }
+ // intersection at endpoint of segment#2?
+ if ( perpSeg1 === 0 ) return [ inSeg2Pt1 ];
+ if ( perpSeg1 === limit ) return [ inSeg2Pt2 ];
+
+ // return real intersection point
+ var factorSeg1 = perpSeg2 / limit;
+ return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx,
+ y: inSeg1Pt1.y + factorSeg1 * seg1dy } ];
+
+ } else {
+
+ // parallel or collinear
+ if ( ( perpSeg1 !== 0 ) ||
+ ( seg2dy * seg1seg2dx !== seg2dx * seg1seg2dy ) ) return [];
+
+ // they are collinear or degenerate
+ var seg1Pt = ( ( seg1dx === 0 ) && ( seg1dy === 0 ) ); // segment1 is just a point?
+ var seg2Pt = ( ( seg2dx === 0 ) && ( seg2dy === 0 ) ); // segment2 is just a point?
+ // both segments are points
+ if ( seg1Pt && seg2Pt ) {
+
+ if ( ( inSeg1Pt1.x !== inSeg2Pt1.x ) ||
+ ( inSeg1Pt1.y !== inSeg2Pt1.y ) ) return []; // they are distinct points
+ return [ inSeg1Pt1 ]; // they are the same point
+
+ }
+ // segment#1 is a single point
+ if ( seg1Pt ) {
+
+ if ( ! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2
+ return [ inSeg1Pt1 ];
+
+ }
+ // segment#2 is a single point
+ if ( seg2Pt ) {
+
+ if ( ! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1
+ return [ inSeg2Pt1 ];
+
+ }
+
+ // they are collinear segments, which might overlap
+ var seg1min, seg1max, seg1minVal, seg1maxVal;
+ var seg2min, seg2max, seg2minVal, seg2maxVal;
+ if ( seg1dx !== 0 ) {
+
+ // the segments are NOT on a vertical line
+ if ( inSeg1Pt1.x < inSeg1Pt2.x ) {
+
+ seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x;
+ seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x;
+
+ } else {
+
+ seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x;
+ seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x;
+
+ }
+ if ( inSeg2Pt1.x < inSeg2Pt2.x ) {
+
+ seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x;
+ seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x;
+
+ } else {
+
+ seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x;
+ seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x;
+
+ }
+
+ } else {
+
+ // the segments are on a vertical line
+ if ( inSeg1Pt1.y < inSeg1Pt2.y ) {
+
+ seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y;
+ seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y;
+
+ } else {
+
+ seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y;
+ seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y;
+
+ }
+ if ( inSeg2Pt1.y < inSeg2Pt2.y ) {
+
+ seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y;
+ seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y;
+
+ } else {
+
+ seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y;
+ seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y;
+
+ }
+
+ }
+ if ( seg1minVal <= seg2minVal ) {
+
+ if ( seg1maxVal < seg2minVal ) return [];
+ if ( seg1maxVal === seg2minVal ) {
+
+ if ( inExcludeAdjacentSegs ) return [];
+ return [ seg2min ];
+
+ }
+ if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ];
+ return [ seg2min, seg2max ];
+
+ } else {
+
+ if ( seg1minVal > seg2maxVal ) return [];
+ if ( seg1minVal === seg2maxVal ) {
+
+ if ( inExcludeAdjacentSegs ) return [];
+ return [ seg1min ];
+
+ }
+ if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ];
+ return [ seg1min, seg2max ];
+
+ }
+
+ }
+
+ }
+
+ function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) {
+
+ // The order of legs is important
+
+ // translation of all points, so that Vertex is at (0,0)
+ var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y;
+ var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y;
+ var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y;
+
+ // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg.
+ var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX;
+ var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX;
+
+ if ( Math.abs( from2toAngle ) > Number.EPSILON ) {
+
+ // angle != 180 deg.
+
+ var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX;
+ // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle );
+
+ if ( from2toAngle > 0 ) {
+
+ // main angle < 180 deg.
+ return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) );
+
+ } else {
+
+ // main angle > 180 deg.
+ return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) );
+
+ }
+
+ } else {
+
+ // angle == 180 deg.
+ // console.log( "from2to: 180 deg., from2other: " + from2otherAngle );
+ return ( from2otherAngle > 0 );
+
+ }
+
+ }
+
+
+ function removeHoles( contour, holes ) {
+
+ var shape = contour.concat(); // work on this shape
+ var hole;
+
+ function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) {
+
+ // Check if hole point lies within angle around shape point
+ var lastShapeIdx = shape.length - 1;
+
+ var prevShapeIdx = inShapeIdx - 1;
+ if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx;
+
+ var nextShapeIdx = inShapeIdx + 1;
+ if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0;
+
+ var insideAngle = isPointInsideAngle( shape[ inShapeIdx ], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[ inHoleIdx ] );
+ if ( ! insideAngle ) {
+
+ // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y );
+ return false;
+
+ }
+
+ // Check if shape point lies within angle around hole point
+ var lastHoleIdx = hole.length - 1;
+
+ var prevHoleIdx = inHoleIdx - 1;
+ if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx;
+
+ var nextHoleIdx = inHoleIdx + 1;
+ if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0;
+
+ insideAngle = isPointInsideAngle( hole[ inHoleIdx ], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[ inShapeIdx ] );
+ if ( ! insideAngle ) {
+
+ // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y );
+ return false;
+
+ }
+
+ return true;
+
+ }
+
+ function intersectsShapeEdge( inShapePt, inHolePt ) {
+
+ // checks for intersections with shape edges
+ var sIdx, nextIdx, intersection;
+ for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) {
+
+ nextIdx = sIdx + 1; nextIdx %= shape.length;
+ intersection = intersect_segments_2D( inShapePt, inHolePt, shape[ sIdx ], shape[ nextIdx ], true );
+ if ( intersection.length > 0 ) return true;
+
+ }
+
+ return false;
+
+ }
+
+ var indepHoles = [];
+
+ function intersectsHoleEdge( inShapePt, inHolePt ) {
+
+ // checks for intersections with hole edges
+ var ihIdx, chkHole,
+ hIdx, nextIdx, intersection;
+ for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) {
+
+ chkHole = holes[ indepHoles[ ihIdx ]];
+ for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) {
+
+ nextIdx = hIdx + 1; nextIdx %= chkHole.length;
+ intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[ hIdx ], chkHole[ nextIdx ], true );
+ if ( intersection.length > 0 ) return true;
+
+ }
+
+ }
+ return false;
+
+ }
+
+ var holeIndex, shapeIndex,
+ shapePt, holePt,
+ holeIdx, cutKey, failedCuts = [],
+ tmpShape1, tmpShape2,
+ tmpHole1, tmpHole2;
+
+ for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
+
+ indepHoles.push( h );
+
+ }
+
+ var minShapeIndex = 0;
+ var counter = indepHoles.length * 2;
+ while ( indepHoles.length > 0 ) {
+
+ counter --;
+ if ( counter < 0 ) {
+
+ console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" );
+ break;
+
+ }
+
+ // search for shape-vertex and hole-vertex,
+ // which can be connected without intersections
+ for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) {
+
+ shapePt = shape[ shapeIndex ];
+ holeIndex = - 1;
+
+ // search for hole which can be reached without intersections
+ for ( var h = 0; h < indepHoles.length; h ++ ) {
+
+ holeIdx = indepHoles[ h ];
+
+ // prevent multiple checks
+ cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx;
+ if ( failedCuts[ cutKey ] !== undefined ) continue;
+
+ hole = holes[ holeIdx ];
+ for ( var h2 = 0; h2 < hole.length; h2 ++ ) {
+
+ holePt = hole[ h2 ];
+ if ( ! isCutLineInsideAngles( shapeIndex, h2 ) ) continue;
+ if ( intersectsShapeEdge( shapePt, holePt ) ) continue;
+ if ( intersectsHoleEdge( shapePt, holePt ) ) continue;
+
+ holeIndex = h2;
+ indepHoles.splice( h, 1 );
+
+ tmpShape1 = shape.slice( 0, shapeIndex + 1 );
+ tmpShape2 = shape.slice( shapeIndex );
+ tmpHole1 = hole.slice( holeIndex );
+ tmpHole2 = hole.slice( 0, holeIndex + 1 );
+
+ shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
+
+ minShapeIndex = shapeIndex;
+
+ // Debug only, to show the selected cuts
+ // glob_CutLines.push( [ shapePt, holePt ] );
+
+ break;
+
+ }
+ if ( holeIndex >= 0 ) break; // hole-vertex found
+
+ failedCuts[ cutKey ] = true; // remember failure
+
+ }
+ if ( holeIndex >= 0 ) break; // hole-vertex found
+
+ }
+
+ }
+
+ return shape; /* shape with no holes */
+
+ }
+
+
+ var i, il, f, face,
+ key, index,
+ allPointsMap = {};
+
+ // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first.
+
+ var allpoints = contour.concat();
+
+ for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
+
+ Array.prototype.push.apply( allpoints, holes[ h ] );
+
+ }
+
+ //console.log( "allpoints",allpoints, allpoints.length );
+
+ // prepare all points map
+
+ for ( i = 0, il = allpoints.length; i < il; i ++ ) {
+
+ key = allpoints[ i ].x + ":" + allpoints[ i ].y;
+
+ if ( allPointsMap[ key ] !== undefined ) {
+
+ console.warn( "THREE.Shape: Duplicate point", key );
+
+ }
+
+ allPointsMap[ key ] = i;
+
+ }
+
+ // remove holes by cutting paths to holes and adding them to the shape
+ var shapeWithoutHoles = removeHoles( contour, holes );
+
+ var triangles = THREE.ShapeUtils.triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape
+ //console.log( "triangles",triangles, triangles.length );
+
+ // check all face vertices against all points map
+
+ for ( i = 0, il = triangles.length; i < il; i ++ ) {
+
+ face = triangles[ i ];
+
+ for ( f = 0; f < 3; f ++ ) {
+
+ key = face[ f ].x + ":" + face[ f ].y;
+
+ index = allPointsMap[ key ];
+
+ if ( index !== undefined ) {
+
+ face[ f ] = index;
+
+ }
+
+ }
+
+ }
+
+ return triangles.concat();
+
+ },
+
+ isClockWise: function ( pts ) {
+
+ return THREE.ShapeUtils.area( pts ) < 0;
+
+ },
+
+ // Bezier Curves formulas obtained from
+ // http://en.wikipedia.org/wiki/B%C3%A9zier_curve
+
+ // Quad Bezier Functions
+
+ b2: ( function () {
+
+ function b2p0( t, p ) {
+
+ var k = 1 - t;
+ return k * k * p;
+
+ }
+
+ function b2p1( t, p ) {
+
+ return 2 * ( 1 - t ) * t * p;
+
+ }
+
+ function b2p2( t, p ) {
+
+ return t * t * p;
+
+ }
+
+ return function ( t, p0, p1, p2 ) {
+
+ return b2p0( t, p0 ) + b2p1( t, p1 ) + b2p2( t, p2 );
+
+ };
+
+ } )(),
+
+ // Cubic Bezier Functions
+
+ b3: ( function () {
+
+ function b3p0( t, p ) {
+
+ var k = 1 - t;
+ return k * k * k * p;
+
+ }
+
+ function b3p1( t, p ) {
+
+ var k = 1 - t;
+ return 3 * k * k * t * p;
+
+ }
+
+ function b3p2( t, p ) {
+
+ var k = 1 - t;
+ return 3 * k * t * t * p;
+
+ }
+
+ function b3p3( t, p ) {
+
+ return t * t * t * p;
+
+ }
+
+ return function ( t, p0, p1, p2, p3 ) {
+
+ return b3p0( t, p0 ) + b3p1( t, p1 ) + b3p2( t, p2 ) + b3p3( t, p3 );
+
+ };
+
+ } )()
+
+};
+
+// File:src/extras/audio/Audio.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Audio = function ( listener ) {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Audio';
+
+ this.context = listener.context;
+ this.source = this.context.createBufferSource();
+ this.source.onended = this.onEnded.bind( this );
+
+ this.gain = this.context.createGain();
+ this.gain.connect( this.context.destination );
+
+ this.panner = this.context.createPanner();
+ this.panner.connect( this.gain );
+
+ this.autoplay = false;
+
+ this.startTime = 0;
+ this.playbackRate = 1;
+ this.isPlaying = false;
+
+};
+
+THREE.Audio.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Audio.prototype.constructor = THREE.Audio;
+
+THREE.Audio.prototype.load = function ( file ) {
+
+ var scope = this;
+
+ var request = new XMLHttpRequest();
+ request.open( 'GET', file, true );
+ request.responseType = 'arraybuffer';
+ request.onload = function ( e ) {
+
+ scope.context.decodeAudioData( this.response, function ( buffer ) {
+
+ scope.source.buffer = buffer;
+
+ if ( scope.autoplay ) scope.play();
+
+ } );
+
+ };
+ request.send();
+
+ return this;
+
+};
+
+THREE.Audio.prototype.play = function () {
+
+ if ( this.isPlaying === true ) {
+
+ console.warn( 'THREE.Audio: Audio is already playing.' );
+ return;
+
+ }
+
+ var source = this.context.createBufferSource();
+
+ source.buffer = this.source.buffer;
+ source.loop = this.source.loop;
+ source.onended = this.source.onended;
+ source.start( 0, this.startTime );
+ source.playbackRate.value = this.playbackRate;
+
+ this.isPlaying = true;
+
+ this.source = source;
+
+ this.connect();
+
+};
+
+THREE.Audio.prototype.pause = function () {
+
+ this.source.stop();
+ this.startTime = this.context.currentTime;
+
+};
+
+THREE.Audio.prototype.stop = function () {
+
+ this.source.stop();
+ this.startTime = 0;
+
+};
+
+THREE.Audio.prototype.connect = function () {
+
+ if ( this.filter !== undefined ) {
+
+ this.source.connect( this.filter );
+ this.filter.connect( this.panner );
+
+ } else {
+
+ this.source.connect( this.panner );
+
+ }
+
+};
+
+THREE.Audio.prototype.disconnect = function () {
+
+ if ( this.filter !== undefined ) {
+
+ this.source.disconnect( this.filter );
+ this.filter.disconnect( this.panner );
+
+ } else {
+
+ this.source.disconnect( this.panner );
+
+ }
+
+};
+
+THREE.Audio.prototype.setFilter = function ( value ) {
+
+ if ( this.isPlaying === true ) {
+
+ this.disconnect();
+ this.filter = value;
+ this.connect();
+
+ } else {
+
+ this.filter = value;
+
+ }
+
+};
+
+THREE.Audio.prototype.getFilter = function () {
+
+ return this.filter;
+
+};
+
+THREE.Audio.prototype.setPlaybackRate = function ( value ) {
+
+ this.playbackRate = value;
+
+ if ( this.isPlaying === true ) {
+
+ this.source.playbackRate.value = this.playbackRate;
+
+ }
+
+};
+
+THREE.Audio.prototype.getPlaybackRate = function () {
+
+ return this.playbackRate;
+
+};
+
+THREE.Audio.prototype.onEnded = function() {
+
+ this.isPlaying = false;
+
+};
+
+THREE.Audio.prototype.setLoop = function ( value ) {
+
+ this.source.loop = value;
+
+};
+
+THREE.Audio.prototype.getLoop = function () {
+
+ return this.source.loop;
+
+};
+
+THREE.Audio.prototype.setRefDistance = function ( value ) {
+
+ this.panner.refDistance = value;
+
+};
+
+THREE.Audio.prototype.getRefDistance = function () {
+
+ return this.panner.refDistance;
+
+};
+
+THREE.Audio.prototype.setRolloffFactor = function ( value ) {
+
+ this.panner.rolloffFactor = value;
+
+};
+
+THREE.Audio.prototype.getRolloffFactor = function () {
+
+ return this.panner.rolloffFactor;
+
+};
+
+THREE.Audio.prototype.setVolume = function ( value ) {
+
+ this.gain.gain.value = value;
+
+};
+
+THREE.Audio.prototype.getVolume = function () {
+
+ return this.gain.gain.value;
+
+};
+
+THREE.Audio.prototype.updateMatrixWorld = ( function () {
+
+ var position = new THREE.Vector3();
+
+ return function updateMatrixWorld( force ) {
+
+ THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
+
+ position.setFromMatrixPosition( this.matrixWorld );
+
+ this.panner.setPosition( position.x, position.y, position.z );
+
+ };
+
+} )();
+
+// File:src/extras/audio/AudioListener.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.AudioListener = function () {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'AudioListener';
+
+ this.context = new ( window.AudioContext || window.webkitAudioContext )();
+
+};
+
+THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype );
+THREE.AudioListener.prototype.constructor = THREE.AudioListener;
+
+THREE.AudioListener.prototype.updateMatrixWorld = ( function () {
+
+ var position = new THREE.Vector3();
+ var quaternion = new THREE.Quaternion();
+ var scale = new THREE.Vector3();
+
+ var orientation = new THREE.Vector3();
+
+ return function updateMatrixWorld( force ) {
+
+ THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
+
+ var listener = this.context.listener;
+ var up = this.up;
+
+ this.matrixWorld.decompose( position, quaternion, scale );
+
+ orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion );
+
+ listener.setPosition( position.x, position.y, position.z );
+ listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z );
+
+ };
+
+} )();
+
+// File:src/extras/core/Curve.js
+
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * Extensible curve object
+ *
+ * Some common of Curve methods
+ * .getPoint(t), getTangent(t)
+ * .getPointAt(u), getTagentAt(u)
+ * .getPoints(), .getSpacedPoints()
+ * .getLength()
+ * .updateArcLengths()
+ *
+ * This following classes subclasses THREE.Curve:
+ *
+ * -- 2d classes --
+ * THREE.LineCurve
+ * THREE.QuadraticBezierCurve
+ * THREE.CubicBezierCurve
+ * THREE.SplineCurve
+ * THREE.ArcCurve
+ * THREE.EllipseCurve
+ *
+ * -- 3d classes --
+ * THREE.LineCurve3
+ * THREE.QuadraticBezierCurve3
+ * THREE.CubicBezierCurve3
+ * THREE.SplineCurve3
+ * THREE.ClosedSplineCurve3
+ *
+ * A series of curves can be represented as a THREE.CurvePath
+ *
+ **/
+
+/**************************************************************
+ * Abstract Curve base class
+ **************************************************************/
+
+THREE.Curve = function () {
+
+};
+
+THREE.Curve.prototype = {
+
+ constructor: THREE.Curve,
+
+ // Virtual base class method to overwrite and implement in subclasses
+ // - t [0 .. 1]
+
+ getPoint: function ( t ) {
+
+ console.warn( "THREE.Curve: Warning, getPoint() not implemented!" );
+ return null;
+
+ },
+
+ // Get point at relative position in curve according to arc length
+ // - u [0 .. 1]
+
+ getPointAt: function ( u ) {
+
+ var t = this.getUtoTmapping( u );
+ return this.getPoint( t );
+
+ },
+
+ // Get sequence of points using getPoint( t )
+
+ getPoints: function ( divisions ) {
+
+ if ( ! divisions ) divisions = 5;
+
+ var d, pts = [];
+
+ for ( d = 0; d <= divisions; d ++ ) {
+
+ pts.push( this.getPoint( d / divisions ) );
+
+ }
+
+ return pts;
+
+ },
+
+ // Get sequence of points using getPointAt( u )
+
+ getSpacedPoints: function ( divisions ) {
+
+ if ( ! divisions ) divisions = 5;
+
+ var d, pts = [];
+
+ for ( d = 0; d <= divisions; d ++ ) {
+
+ pts.push( this.getPointAt( d / divisions ) );
+
+ }
+
+ return pts;
+
+ },
+
+ // Get total curve arc length
+
+ getLength: function () {
+
+ var lengths = this.getLengths();
+ return lengths[ lengths.length - 1 ];
+
+ },
+
+ // Get list of cumulative segment lengths
+
+ getLengths: function ( divisions ) {
+
+ if ( ! divisions ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200;
+
+ if ( this.cacheArcLengths
+ && ( this.cacheArcLengths.length === divisions + 1 )
+ && ! this.needsUpdate ) {
+
+ //console.log( "cached", this.cacheArcLengths );
+ return this.cacheArcLengths;
+
+ }
+
+ this.needsUpdate = false;
+
+ var cache = [];
+ var current, last = this.getPoint( 0 );
+ var p, sum = 0;
+
+ cache.push( 0 );
+
+ for ( p = 1; p <= divisions; p ++ ) {
+
+ current = this.getPoint ( p / divisions );
+ sum += current.distanceTo( last );
+ cache.push( sum );
+ last = current;
+
+ }
+
+ this.cacheArcLengths = cache;
+
+ return cache; // { sums: cache, sum:sum }; Sum is in the last element.
+
+ },
+
+ updateArcLengths: function() {
+
+ this.needsUpdate = true;
+ this.getLengths();
+
+ },
+
+ // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant
+
+ getUtoTmapping: function ( u, distance ) {
+
+ var arcLengths = this.getLengths();
+
+ var i = 0, il = arcLengths.length;
+
+ var targetArcLength; // The targeted u distance value to get
+
+ if ( distance ) {
+
+ targetArcLength = distance;
+
+ } else {
+
+ targetArcLength = u * arcLengths[ il - 1 ];
+
+ }
+
+ //var time = Date.now();
+
+ // binary search for the index with largest value smaller than target u distance
+
+ var low = 0, high = il - 1, comparison;
+
+ while ( low <= high ) {
+
+ i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
+
+ comparison = arcLengths[ i ] - targetArcLength;
+
+ if ( comparison < 0 ) {
+
+ low = i + 1;
+
+ } else if ( comparison > 0 ) {
+
+ high = i - 1;
+
+ } else {
+
+ high = i;
+ break;
+
+ // DONE
+
+ }
+
+ }
+
+ i = high;
+
+ //console.log('b' , i, low, high, Date.now()- time);
+
+ if ( arcLengths[ i ] === targetArcLength ) {
+
+ var t = i / ( il - 1 );
+ return t;
+
+ }
+
+ // we could get finer grain at lengths, or use simple interpolation between two points
+
+ var lengthBefore = arcLengths[ i ];
+ var lengthAfter = arcLengths[ i + 1 ];
+
+ var segmentLength = lengthAfter - lengthBefore;
+
+ // determine where we are between the 'before' and 'after' points
+
+ var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
+
+ // add that fractional amount to t
+
+ var t = ( i + segmentFraction ) / ( il - 1 );
+
+ return t;
+
+ },
+
+ // Returns a unit vector tangent at t
+ // In case any sub curve does not implement its tangent derivation,
+ // 2 points a small delta apart will be used to find its gradient
+ // which seems to give a reasonable approximation
+
+ getTangent: function( t ) {
+
+ var delta = 0.0001;
+ var t1 = t - delta;
+ var t2 = t + delta;
+
+ // Capping in case of danger
+
+ if ( t1 < 0 ) t1 = 0;
+ if ( t2 > 1 ) t2 = 1;
+
+ var pt1 = this.getPoint( t1 );
+ var pt2 = this.getPoint( t2 );
+
+ var vec = pt2.clone().sub( pt1 );
+ return vec.normalize();
+
+ },
+
+ getTangentAt: function ( u ) {
+
+ var t = this.getUtoTmapping( u );
+ return this.getTangent( t );
+
+ }
+
+}
+
+THREE.Curve.Utils = THREE.CurveUtils; // backwards compatibility
+
+// TODO: Transformation for Curves?
+
+/**************************************************************
+ * 3D Curves
+ **************************************************************/
+
+// A Factory method for creating new curve subclasses
+
+THREE.Curve.create = function ( constructor, getPointFunc ) {
+
+ constructor.prototype = Object.create( THREE.Curve.prototype );
+ constructor.prototype.constructor = constructor;
+ constructor.prototype.getPoint = getPointFunc;
+
+ return constructor;
+
+};
+
+// File:src/extras/core/CurvePath.js
+
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ *
+ **/
+
+/**************************************************************
+ * Curved Path - a curve path is simply a array of connected
+ * curves, but retains the api of a curve
+ **************************************************************/
+
+THREE.CurvePath = function () {
+
+ this.curves = [];
+
+ this.autoClose = false; // Automatically closes the path
+
+};
+
+THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype );
+THREE.CurvePath.prototype.constructor = THREE.CurvePath;
+
+THREE.CurvePath.prototype.add = function ( curve ) {
+
+ this.curves.push( curve );
+
+};
+
+/*
+THREE.CurvePath.prototype.checkConnection = function() {
+ // TODO
+ // If the ending of curve is not connected to the starting
+ // or the next curve, then, this is not a real path
+};
+*/
+
+THREE.CurvePath.prototype.closePath = function() {
+
+ // TODO Test
+ // and verify for vector3 (needs to implement equals)
+ // Add a line curve if start and end of lines are not connected
+ var startPoint = this.curves[ 0 ].getPoint( 0 );
+ var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 );
+
+ if ( ! startPoint.equals( endPoint ) ) {
+
+ this.curves.push( new THREE.LineCurve( endPoint, startPoint ) );
+
+ }
+
+};
+
+// To get accurate point with reference to
+// entire path distance at time t,
+// following has to be done:
+
+// 1. Length of each sub path have to be known
+// 2. Locate and identify type of curve
+// 3. Get t for the curve
+// 4. Return curve.getPointAt(t')
+
+THREE.CurvePath.prototype.getPoint = function( t ) {
+
+ var d = t * this.getLength();
+ var curveLengths = this.getCurveLengths();
+ var i = 0;
+
+ // To think about boundaries points.
+
+ while ( i < curveLengths.length ) {
+
+ if ( curveLengths[ i ] >= d ) {
+
+ var diff = curveLengths[ i ] - d;
+ var curve = this.curves[ i ];
+
+ var u = 1 - diff / curve.getLength();
+
+ return curve.getPointAt( u );
+
+ }
+
+ i ++;
+
+ }
+
+ return null;
+
+ // loop where sum != 0, sum > d , sum+1 0 ) {
+
+ laste = points[ points.length - 1 ];
+
+ cpx0 = laste.x;
+ cpy0 = laste.y;
+
+ } else {
+
+ laste = this.actions[ i - 1 ].args;
+
+ cpx0 = laste[ laste.length - 2 ];
+ cpy0 = laste[ laste.length - 1 ];
+
+ }
+
+ for ( var j = 1; j <= divisions; j ++ ) {
+
+ var t = j / divisions;
+
+ tx = b2( t, cpx0, cpx1, cpx );
+ ty = b2( t, cpy0, cpy1, cpy );
+
+ points.push( new THREE.Vector2( tx, ty ) );
+
+ }
+
+ break;
+
+ case 'bezierCurveTo':
+
+ cpx = args[ 4 ];
+ cpy = args[ 5 ];
+
+ cpx1 = args[ 0 ];
+ cpy1 = args[ 1 ];
+
+ cpx2 = args[ 2 ];
+ cpy2 = args[ 3 ];
+
+ if ( points.length > 0 ) {
+
+ laste = points[ points.length - 1 ];
+
+ cpx0 = laste.x;
+ cpy0 = laste.y;
+
+ } else {
+
+ laste = this.actions[ i - 1 ].args;
+
+ cpx0 = laste[ laste.length - 2 ];
+ cpy0 = laste[ laste.length - 1 ];
+
+ }
+
+
+ for ( var j = 1; j <= divisions; j ++ ) {
+
+ var t = j / divisions;
+
+ tx = b3( t, cpx0, cpx1, cpx2, cpx );
+ ty = b3( t, cpy0, cpy1, cpy2, cpy );
+
+ points.push( new THREE.Vector2( tx, ty ) );
+
+ }
+
+ break;
+
+ case 'splineThru':
+
+ laste = this.actions[ i - 1 ].args;
+
+ var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] );
+ var spts = [ last ];
+
+ var n = divisions * args[ 0 ].length;
+
+ spts = spts.concat( args[ 0 ] );
+
+ var spline = new THREE.SplineCurve( spts );
+
+ for ( var j = 1; j <= n; j ++ ) {
+
+ points.push( spline.getPointAt( j / n ) );
+
+ }
+
+ break;
+
+ case 'arc':
+
+ var aX = args[ 0 ], aY = args[ 1 ],
+ aRadius = args[ 2 ],
+ aStartAngle = args[ 3 ], aEndAngle = args[ 4 ],
+ aClockwise = !! args[ 5 ];
+
+ var deltaAngle = aEndAngle - aStartAngle;
+ var angle;
+ var tdivisions = divisions * 2;
+
+ for ( var j = 1; j <= tdivisions; j ++ ) {
+
+ var t = j / tdivisions;
+
+ if ( ! aClockwise ) {
+
+ t = 1 - t;
+
+ }
+
+ angle = aStartAngle + t * deltaAngle;
+
+ tx = aX + aRadius * Math.cos( angle );
+ ty = aY + aRadius * Math.sin( angle );
+
+ //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
+
+ points.push( new THREE.Vector2( tx, ty ) );
+
+ }
+
+ //console.log(points);
+
+ break;
+
+ case 'ellipse':
+
+ var aX = args[ 0 ], aY = args[ 1 ],
+ xRadius = args[ 2 ],
+ yRadius = args[ 3 ],
+ aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
+ aClockwise = !! args[ 6 ],
+ aRotation = args[ 7 ];
+
+
+ var deltaAngle = aEndAngle - aStartAngle;
+ var angle;
+ var tdivisions = divisions * 2;
+
+ var cos, sin;
+ if ( aRotation !== 0 ) {
+
+ cos = Math.cos( aRotation );
+ sin = Math.sin( aRotation );
+
+ }
+
+ for ( var j = 1; j <= tdivisions; j ++ ) {
+
+ var t = j / tdivisions;
+
+ if ( ! aClockwise ) {
+
+ t = 1 - t;
+
+ }
+
+ angle = aStartAngle + t * deltaAngle;
+
+ tx = aX + xRadius * Math.cos( angle );
+ ty = aY + yRadius * Math.sin( angle );
+
+ if ( aRotation !== 0 ) {
+
+ var x = tx, y = ty;
+
+ // Rotate the point about the center of the ellipse.
+ tx = ( x - aX ) * cos - ( y - aY ) * sin + aX;
+ ty = ( x - aX ) * sin + ( y - aY ) * cos + aY;
+
+ }
+
+ //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
+
+ points.push( new THREE.Vector2( tx, ty ) );
+
+ }
+
+ //console.log(points);
+
+ break;
+
+ } // end switch
+
+ }
+
+
+
+ // Normalize to remove the closing point by default.
+ var lastPoint = points[ points.length - 1 ];
+ if ( Math.abs( lastPoint.x - points[ 0 ].x ) < Number.EPSILON &&
+ Math.abs( lastPoint.y - points[ 0 ].y ) < Number.EPSILON )
+ points.splice( points.length - 1, 1 );
+ if ( closedPath ) {
+
+ points.push( points[ 0 ] );
+
+ }
+
+ return points;
+
+};
+
+//
+// Breaks path into shapes
+//
+// Assumptions (if parameter isCCW==true the opposite holds):
+// - solid shapes are defined clockwise (CW)
+// - holes are defined counterclockwise (CCW)
+//
+// If parameter noHoles==true:
+// - all subPaths are regarded as solid shapes
+// - definition order CW/CCW has no relevance
+//
+
+THREE.Path.prototype.toShapes = function( isCCW, noHoles ) {
+
+ function extractSubpaths( inActions ) {
+
+ var subPaths = [], lastPath = new THREE.Path();
+
+ for ( var i = 0, l = inActions.length; i < l; i ++ ) {
+
+ var item = inActions[ i ];
+
+ var args = item.args;
+ var action = item.action;
+
+ if ( action === 'moveTo' ) {
+
+ if ( lastPath.actions.length !== 0 ) {
+
+ subPaths.push( lastPath );
+ lastPath = new THREE.Path();
+
+ }
+
+ }
+
+ lastPath[ action ].apply( lastPath, args );
+
+ }
+
+ if ( lastPath.actions.length !== 0 ) {
+
+ subPaths.push( lastPath );
+
+ }
+
+ // console.log(subPaths);
+
+ return subPaths;
+
+ }
+
+ function toShapesNoHoles( inSubpaths ) {
+
+ var shapes = [];
+
+ for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) {
+
+ var tmpPath = inSubpaths[ i ];
+
+ var tmpShape = new THREE.Shape();
+ tmpShape.actions = tmpPath.actions;
+ tmpShape.curves = tmpPath.curves;
+
+ shapes.push( tmpShape );
+
+ }
+
+ //console.log("shape", shapes);
+
+ return shapes;
+
+ }
+
+ function isPointInsidePolygon( inPt, inPolygon ) {
+
+ var polyLen = inPolygon.length;
+
+ // inPt on polygon contour => immediate success or
+ // toggling of inside/outside at every single! intersection point of an edge
+ // with the horizontal line through inPt, left of inPt
+ // not counting lowerY endpoints of edges and whole edges on that line
+ var inside = false;
+ for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {
+
+ var edgeLowPt = inPolygon[ p ];
+ var edgeHighPt = inPolygon[ q ];
+
+ var edgeDx = edgeHighPt.x - edgeLowPt.x;
+ var edgeDy = edgeHighPt.y - edgeLowPt.y;
+
+ if ( Math.abs( edgeDy ) > Number.EPSILON ) {
+
+ // not parallel
+ if ( edgeDy < 0 ) {
+
+ edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx;
+ edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;
+
+ }
+ if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue;
+
+ if ( inPt.y === edgeLowPt.y ) {
+
+ if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ?
+ // continue; // no intersection or edgeLowPt => doesn't count !!!
+
+ } else {
+
+ var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y );
+ if ( perpEdge === 0 ) return true; // inPt is on contour ?
+ if ( perpEdge < 0 ) continue;
+ inside = ! inside; // true intersection left of inPt
+
+ }
+
+ } else {
+
+ // parallel or collinear
+ if ( inPt.y !== edgeLowPt.y ) continue; // parallel
+ // edge lies on the same horizontal line as inPt
+ if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||
+ ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour !
+ // continue;
+
+ }
+
+ }
+
+ return inside;
+
+ }
+
+ var isClockWise = THREE.ShapeUtils.isClockWise;
+
+ var subPaths = extractSubpaths( this.actions );
+ if ( subPaths.length === 0 ) return [];
+
+ if ( noHoles === true ) return toShapesNoHoles( subPaths );
+
+
+ var solid, tmpPath, tmpShape, shapes = [];
+
+ if ( subPaths.length === 1 ) {
+
+ tmpPath = subPaths[ 0 ];
+ tmpShape = new THREE.Shape();
+ tmpShape.actions = tmpPath.actions;
+ tmpShape.curves = tmpPath.curves;
+ shapes.push( tmpShape );
+ return shapes;
+
+ }
+
+ var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() );
+ holesFirst = isCCW ? ! holesFirst : holesFirst;
+
+ // console.log("Holes first", holesFirst);
+
+ var betterShapeHoles = [];
+ var newShapes = [];
+ var newShapeHoles = [];
+ var mainIdx = 0;
+ var tmpPoints;
+
+ newShapes[ mainIdx ] = undefined;
+ newShapeHoles[ mainIdx ] = [];
+
+ for ( var i = 0, l = subPaths.length; i < l; i ++ ) {
+
+ tmpPath = subPaths[ i ];
+ tmpPoints = tmpPath.getPoints();
+ solid = isClockWise( tmpPoints );
+ solid = isCCW ? ! solid : solid;
+
+ if ( solid ) {
+
+ if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++;
+
+ newShapes[ mainIdx ] = { s: new THREE.Shape(), p: tmpPoints };
+ newShapes[ mainIdx ].s.actions = tmpPath.actions;
+ newShapes[ mainIdx ].s.curves = tmpPath.curves;
+
+ if ( holesFirst ) mainIdx ++;
+ newShapeHoles[ mainIdx ] = [];
+
+ //console.log('cw', i);
+
+ } else {
+
+ newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } );
+
+ //console.log('ccw', i);
+
+ }
+
+ }
+
+ // only Holes? -> probably all Shapes with wrong orientation
+ if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths );
+
+
+ if ( newShapes.length > 1 ) {
+
+ var ambiguous = false;
+ var toChange = [];
+
+ for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
+
+ betterShapeHoles[ sIdx ] = [];
+
+ }
+
+ for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
+
+ var sho = newShapeHoles[ sIdx ];
+
+ for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) {
+
+ var ho = sho[ hIdx ];
+ var hole_unassigned = true;
+
+ for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {
+
+ if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) {
+
+ if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );
+ if ( hole_unassigned ) {
+
+ hole_unassigned = false;
+ betterShapeHoles[ s2Idx ].push( ho );
+
+ } else {
+
+ ambiguous = true;
+
+ }
+
+ }
+
+ }
+ if ( hole_unassigned ) {
+
+ betterShapeHoles[ sIdx ].push( ho );
+
+ }
+
+ }
+
+ }
+ // console.log("ambiguous: ", ambiguous);
+ if ( toChange.length > 0 ) {
+
+ // console.log("to change: ", toChange);
+ if ( ! ambiguous ) newShapeHoles = betterShapeHoles;
+
+ }
+
+ }
+
+ var tmpHoles;
+
+ for ( var i = 0, il = newShapes.length; i < il; i ++ ) {
+
+ tmpShape = newShapes[ i ].s;
+ shapes.push( tmpShape );
+ tmpHoles = newShapeHoles[ i ];
+
+ for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) {
+
+ tmpShape.holes.push( tmpHoles[ j ].h );
+
+ }
+
+ }
+
+ //console.log("shape", shapes);
+
+ return shapes;
+
+};
+
+// File:src/extras/core/Shape.js
+
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * Defines a 2d shape plane using paths.
+ **/
+
+// STEP 1 Create a path.
+// STEP 2 Turn path into shape.
+// STEP 3 ExtrudeGeometry takes in Shape/Shapes
+// STEP 3a - Extract points from each shape, turn to vertices
+// STEP 3b - Triangulate each shape, add faces.
+
+THREE.Shape = function () {
+
+ THREE.Path.apply( this, arguments );
+
+ this.holes = [];
+
+};
+
+THREE.Shape.prototype = Object.create( THREE.Path.prototype );
+THREE.Shape.prototype.constructor = THREE.Shape;
+
+// Convenience method to return ExtrudeGeometry
+
+THREE.Shape.prototype.extrude = function ( options ) {
+
+ return new THREE.ExtrudeGeometry( this, options );
+
+};
+
+// Convenience method to return ShapeGeometry
+
+THREE.Shape.prototype.makeGeometry = function ( options ) {
+
+ return new THREE.ShapeGeometry( this, options );
+
+};
+
+// Get points of holes
+
+THREE.Shape.prototype.getPointsHoles = function ( divisions ) {
+
+ var holesPts = [];
+
+ for ( var i = 0, l = this.holes.length; i < l; i ++ ) {
+
+ holesPts[ i ] = this.holes[ i ].getPoints( divisions );
+
+ }
+
+ return holesPts;
+
+};
+
+
+// Get points of shape and holes (keypoints based on segments parameter)
+
+THREE.Shape.prototype.extractAllPoints = function ( divisions ) {
+
+ return {
+
+ shape: this.getPoints( divisions ),
+ holes: this.getPointsHoles( divisions )
+
+ };
+
+};
+
+THREE.Shape.prototype.extractPoints = function ( divisions ) {
+
+ return this.extractAllPoints( divisions );
+
+};
+
+THREE.Shape.Utils = THREE.ShapeUtils; // backwards compatibility
+
+// File:src/extras/curves/LineCurve.js
+
+/**************************************************************
+ * Line
+ **************************************************************/
+
+THREE.LineCurve = function ( v1, v2 ) {
+
+ this.v1 = v1;
+ this.v2 = v2;
+
+};
+
+THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.LineCurve.prototype.constructor = THREE.LineCurve;
+
+THREE.LineCurve.prototype.getPoint = function ( t ) {
+
+ var point = this.v2.clone().sub( this.v1 );
+ point.multiplyScalar( t ).add( this.v1 );
+
+ return point;
+
+};
+
+// Line curve is linear, so we can overwrite default getPointAt
+
+THREE.LineCurve.prototype.getPointAt = function ( u ) {
+
+ return this.getPoint( u );
+
+};
+
+THREE.LineCurve.prototype.getTangent = function( t ) {
+
+ var tangent = this.v2.clone().sub( this.v1 );
+
+ return tangent.normalize();
+
+};
+
+// File:src/extras/curves/QuadraticBezierCurve.js
+
+/**************************************************************
+ * Quadratic Bezier curve
+ **************************************************************/
+
+
+THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) {
+
+ this.v0 = v0;
+ this.v1 = v1;
+ this.v2 = v2;
+
+};
+
+THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.QuadraticBezierCurve.prototype.constructor = THREE.QuadraticBezierCurve;
+
+
+THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) {
+
+ var b2 = THREE.ShapeUtils.b2;
+
+ return new THREE.Vector2(
+ b2( t, this.v0.x, this.v1.x, this.v2.x ),
+ b2( t, this.v0.y, this.v1.y, this.v2.y )
+ );
+
+};
+
+
+THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) {
+
+ var tangentQuadraticBezier = THREE.CurveUtils.tangentQuadraticBezier;
+
+ return new THREE.Vector2(
+ tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ),
+ tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y )
+ ).normalize();
+
+};
+
+// File:src/extras/curves/CubicBezierCurve.js
+
+/**************************************************************
+ * Cubic Bezier curve
+ **************************************************************/
+
+THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) {
+
+ this.v0 = v0;
+ this.v1 = v1;
+ this.v2 = v2;
+ this.v3 = v3;
+
+};
+
+THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.CubicBezierCurve.prototype.constructor = THREE.CubicBezierCurve;
+
+THREE.CubicBezierCurve.prototype.getPoint = function ( t ) {
+
+ var b3 = THREE.ShapeUtils.b3;
+
+ return new THREE.Vector2(
+ b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ),
+ b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y )
+ );
+
+};
+
+THREE.CubicBezierCurve.prototype.getTangent = function( t ) {
+
+ var tangentCubicBezier = THREE.CurveUtils.tangentCubicBezier;
+
+ return new THREE.Vector2(
+ tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ),
+ tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y )
+ ).normalize();
+
+};
+
+// File:src/extras/curves/SplineCurve.js
+
+/**************************************************************
+ * Spline curve
+ **************************************************************/
+
+THREE.SplineCurve = function ( points /* array of Vector2 */ ) {
+
+ this.points = ( points == undefined ) ? [] : points;
+
+};
+
+THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.SplineCurve.prototype.constructor = THREE.SplineCurve;
+
+THREE.SplineCurve.prototype.getPoint = function ( t ) {
+
+ var points = this.points;
+ var point = ( points.length - 1 ) * t;
+
+ var intPoint = Math.floor( point );
+ var weight = point - intPoint;
+
+ var point0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ];
+ var point1 = points[ intPoint ];
+ var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];
+ var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];
+
+ var interpolate = THREE.CurveUtils.interpolate;
+
+ return new THREE.Vector2(
+ interpolate( point0.x, point1.x, point2.x, point3.x, weight ),
+ interpolate( point0.y, point1.y, point2.y, point3.y, weight )
+ );
+
+};
+
+// File:src/extras/curves/EllipseCurve.js
+
+/**************************************************************
+ * Ellipse curve
+ **************************************************************/
+
+THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {
+
+ this.aX = aX;
+ this.aY = aY;
+
+ this.xRadius = xRadius;
+ this.yRadius = yRadius;
+
+ this.aStartAngle = aStartAngle;
+ this.aEndAngle = aEndAngle;
+
+ this.aClockwise = aClockwise;
+
+ this.aRotation = aRotation || 0;
+
+};
+
+THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.EllipseCurve.prototype.constructor = THREE.EllipseCurve;
+
+THREE.EllipseCurve.prototype.getPoint = function ( t ) {
+
+ var deltaAngle = this.aEndAngle - this.aStartAngle;
+
+ if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2;
+ if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2;
+
+ var angle;
+
+ if ( this.aClockwise === true ) {
+
+ angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle );
+
+ } else {
+
+ angle = this.aStartAngle + t * deltaAngle;
+
+ }
+
+ var x = this.aX + this.xRadius * Math.cos( angle );
+ var y = this.aY + this.yRadius * Math.sin( angle );
+
+ if ( this.aRotation !== 0 ) {
+
+ var cos = Math.cos( this.aRotation );
+ var sin = Math.sin( this.aRotation );
+
+ var tx = x, ty = y;
+
+ // Rotate the point about the center of the ellipse.
+ x = ( tx - this.aX ) * cos - ( ty - this.aY ) * sin + this.aX;
+ y = ( tx - this.aX ) * sin + ( ty - this.aY ) * cos + this.aY;
+
+ }
+
+ return new THREE.Vector2( x, y );
+
+};
+
+// File:src/extras/curves/ArcCurve.js
+
+/**************************************************************
+ * Arc curve
+ **************************************************************/
+
+THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
+
+ THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
+
+};
+
+THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype );
+THREE.ArcCurve.prototype.constructor = THREE.ArcCurve;
+
+// File:src/extras/curves/LineCurve3.js
+
+/**************************************************************
+ * Line3D
+ **************************************************************/
+
+THREE.LineCurve3 = THREE.Curve.create(
+
+ function ( v1, v2 ) {
+
+ this.v1 = v1;
+ this.v2 = v2;
+
+ },
+
+ function ( t ) {
+
+ var vector = new THREE.Vector3();
+
+ vector.subVectors( this.v2, this.v1 ); // diff
+ vector.multiplyScalar( t );
+ vector.add( this.v1 );
+
+ return vector;
+
+ }
+
+);
+
+// File:src/extras/curves/QuadraticBezierCurve3.js
+
+/**************************************************************
+ * Quadratic Bezier 3D curve
+ **************************************************************/
+
+THREE.QuadraticBezierCurve3 = THREE.Curve.create(
+
+ function ( v0, v1, v2 ) {
+
+ this.v0 = v0;
+ this.v1 = v1;
+ this.v2 = v2;
+
+ },
+
+ function ( t ) {
+
+ var b2 = THREE.ShapeUtils.b2;
+
+ return new THREE.Vector3(
+ b2( t, this.v0.x, this.v1.x, this.v2.x ),
+ b2( t, this.v0.y, this.v1.y, this.v2.y ),
+ b2( t, this.v0.z, this.v1.z, this.v2.z )
+ );
+
+ }
+
+);
+
+// File:src/extras/curves/CubicBezierCurve3.js
+
+/**************************************************************
+ * Cubic Bezier 3D curve
+ **************************************************************/
+
+THREE.CubicBezierCurve3 = THREE.Curve.create(
+
+ function ( v0, v1, v2, v3 ) {
+
+ this.v0 = v0;
+ this.v1 = v1;
+ this.v2 = v2;
+ this.v3 = v3;
+
+ },
+
+ function ( t ) {
+
+ var b3 = THREE.ShapeUtils.b3;
+
+ return new THREE.Vector3(
+ b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ),
+ b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ),
+ b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z )
+ );
+
+ }
+
+);
+
+// File:src/extras/curves/SplineCurve3.js
+
+/**************************************************************
+ * Spline 3D curve
+ **************************************************************/
+
+
+THREE.SplineCurve3 = THREE.Curve.create(
+
+ function ( points /* array of Vector3 */ ) {
+
+ console.warn( 'THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3' );
+ this.points = ( points == undefined ) ? [] : points;
+
+ },
+
+ function ( t ) {
+
+ var points = this.points;
+ var point = ( points.length - 1 ) * t;
+
+ var intPoint = Math.floor( point );
+ var weight = point - intPoint;
+
+ var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ];
+ var point1 = points[ intPoint ];
+ var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];
+ var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];
+
+ var interpolate = THREE.CurveUtils.interpolate;
+
+ return new THREE.Vector3(
+ interpolate( point0.x, point1.x, point2.x, point3.x, weight ),
+ interpolate( point0.y, point1.y, point2.y, point3.y, weight ),
+ interpolate( point0.z, point1.z, point2.z, point3.z, weight )
+ );
+
+ }
+
+);
+
+// File:src/extras/curves/CatmullRomCurve3.js
+
+/**
+ * @author zz85 https://github.com/zz85
+ *
+ * Centripetal CatmullRom Curve - which is useful for avoiding
+ * cusps and self-intersections in non-uniform catmull rom curves.
+ * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
+ *
+ * curve.type accepts centripetal(default), chordal and catmullrom
+ * curve.tension is used for catmullrom which defaults to 0.5
+ */
+
+THREE.CatmullRomCurve3 = ( function() {
+
+ var
+ tmp = new THREE.Vector3(),
+ px = new CubicPoly(),
+ py = new CubicPoly(),
+ pz = new CubicPoly();
+
+ /*
+ Based on an optimized c++ solution in
+ - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/
+ - http://ideone.com/NoEbVM
+
+ This CubicPoly class could be used for reusing some variables and calculations,
+ but for three.js curve use, it could be possible inlined and flatten into a single function call
+ which can be placed in CurveUtils.
+ */
+
+ function CubicPoly() {
+
+ }
+
+ /*
+ * Compute coefficients for a cubic polynomial
+ * p(s) = c0 + c1*s + c2*s^2 + c3*s^3
+ * such that
+ * p(0) = x0, p(1) = x1
+ * and
+ * p'(0) = t0, p'(1) = t1.
+ */
+ CubicPoly.prototype.init = function( x0, x1, t0, t1 ) {
+
+ this.c0 = x0;
+ this.c1 = t0;
+ this.c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1;
+ this.c3 = 2 * x0 - 2 * x1 + t0 + t1;
+
+ };
+
+ CubicPoly.prototype.initNonuniformCatmullRom = function( x0, x1, x2, x3, dt0, dt1, dt2 ) {
+
+ // compute tangents when parameterized in [t1,t2]
+ var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1;
+ var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2;
+
+ // rescale tangents for parametrization in [0,1]
+ t1 *= dt1;
+ t2 *= dt1;
+
+ // initCubicPoly
+ this.init( x1, x2, t1, t2 );
+
+ };
+
+ // standard Catmull-Rom spline: interpolate between x1 and x2 with previous/following points x1/x4
+ CubicPoly.prototype.initCatmullRom = function( x0, x1, x2, x3, tension ) {
+
+ this.init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) );
+
+ };
+
+ CubicPoly.prototype.calc = function( t ) {
+
+ var t2 = t * t;
+ var t3 = t2 * t;
+ return this.c0 + this.c1 * t + this.c2 * t2 + this.c3 * t3;
+
+ };
+
+ // Subclass Three.js curve
+ return THREE.Curve.create(
+
+ function ( p /* array of Vector3 */ ) {
+
+ this.points = p || [];
+
+ },
+
+ function ( t ) {
+
+ var points = this.points,
+ point, intPoint, weight, l;
+
+ l = points.length;
+
+ if ( l < 2 ) console.log( 'duh, you need at least 2 points' );
+
+ point = ( l - 1 ) * t;
+ intPoint = Math.floor( point );
+ weight = point - intPoint;
+
+ if ( weight === 0 && intPoint === l - 1 ) {
+
+ intPoint = l - 2;
+ weight = 1;
+
+ }
+
+ var p0, p1, p2, p3;
+
+ if ( intPoint === 0 ) {
+
+ // extrapolate first point
+ tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] );
+ p0 = tmp;
+
+ } else {
+
+ p0 = points[ intPoint - 1 ];
+
+ }
+
+ p1 = points[ intPoint ];
+ p2 = points[ intPoint + 1 ];
+
+ if ( intPoint + 2 < l ) {
+
+ p3 = points[ intPoint + 2 ]
+
+ } else {
+
+ // extrapolate last point
+ tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 2 ] );
+ p3 = tmp;
+
+ }
+
+ if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) {
+
+ // init Centripetal / Chordal Catmull-Rom
+ var pow = this.type === 'chordal' ? 0.5 : 0.25;
+ var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow );
+ var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow );
+ var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow );
+
+ // safety check for repeated points
+ if ( dt1 < 1e-4 ) dt1 = 1.0;
+ if ( dt0 < 1e-4 ) dt0 = dt1;
+ if ( dt2 < 1e-4 ) dt2 = dt1;
+
+ px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 );
+ py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 );
+ pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 );
+
+ } else if ( this.type === 'catmullrom' ) {
+
+ var tension = this.tension !== undefined ? this.tension : 0.5;
+ px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, tension );
+ py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, tension );
+ pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, tension );
+
+ }
+
+ var v = new THREE.Vector3(
+ px.calc( weight ),
+ py.calc( weight ),
+ pz.calc( weight )
+ );
+
+ return v;
+
+ }
+
+ );
+
+} )();
+
+// File:src/extras/curves/ClosedSplineCurve3.js
+
+/**************************************************************
+ * Closed Spline 3D curve
+ **************************************************************/
+
+
+THREE.ClosedSplineCurve3 = THREE.Curve.create(
+
+ function ( points /* array of Vector3 */ ) {
+
+ this.points = ( points == undefined ) ? [] : points;
+
+ },
+
+ function ( t ) {
+
+ var points = this.points;
+ var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1
+
+ var intPoint = Math.floor( point );
+ var weight = point - intPoint;
+
+ intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;
+
+ var point0 = points[ ( intPoint - 1 ) % points.length ];
+ var point1 = points[ ( intPoint ) % points.length ];
+ var point2 = points[ ( intPoint + 1 ) % points.length ];
+ var point3 = points[ ( intPoint + 2 ) % points.length ];
+
+ var interpolate = THREE.CurveUtils.interpolate;
+
+ return new THREE.Vector3(
+ interpolate( point0.x, point1.x, point2.x, point3.x, weight ),
+ interpolate( point0.y, point1.y, point2.y, point3.y, weight ),
+ interpolate( point0.z, point1.z, point2.z, point3.z, weight )
+ );
+
+ }
+
+);
+
+// File:src/extras/geometries/BoxGeometry.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as
+ */
+
+THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'BoxGeometry';
+
+ this.parameters = {
+ width: width,
+ height: height,
+ depth: depth,
+ widthSegments: widthSegments,
+ heightSegments: heightSegments,
+ depthSegments: depthSegments
+ };
+
+ this.widthSegments = widthSegments || 1;
+ this.heightSegments = heightSegments || 1;
+ this.depthSegments = depthSegments || 1;
+
+ var scope = this;
+
+ var width_half = width / 2;
+ var height_half = height / 2;
+ var depth_half = depth / 2;
+
+ buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, 0 ); // px
+ buildPlane( 'z', 'y', 1, - 1, depth, height, - width_half, 1 ); // nx
+ buildPlane( 'x', 'z', 1, 1, width, depth, height_half, 2 ); // py
+ buildPlane( 'x', 'z', 1, - 1, width, depth, - height_half, 3 ); // ny
+ buildPlane( 'x', 'y', 1, - 1, width, height, depth_half, 4 ); // pz
+ buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, 5 ); // nz
+
+ function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) {
+
+ var w, ix, iy,
+ gridX = scope.widthSegments,
+ gridY = scope.heightSegments,
+ width_half = width / 2,
+ height_half = height / 2,
+ offset = scope.vertices.length;
+
+ if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) {
+
+ w = 'z';
+
+ } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) {
+
+ w = 'y';
+ gridY = scope.depthSegments;
+
+ } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) {
+
+ w = 'x';
+ gridX = scope.depthSegments;
+
+ }
+
+ var gridX1 = gridX + 1,
+ gridY1 = gridY + 1,
+ segment_width = width / gridX,
+ segment_height = height / gridY,
+ normal = new THREE.Vector3();
+
+ normal[ w ] = depth > 0 ? 1 : - 1;
+
+ for ( iy = 0; iy < gridY1; iy ++ ) {
+
+ for ( ix = 0; ix < gridX1; ix ++ ) {
+
+ var vector = new THREE.Vector3();
+ vector[ u ] = ( ix * segment_width - width_half ) * udir;
+ vector[ v ] = ( iy * segment_height - height_half ) * vdir;
+ vector[ w ] = depth;
+
+ scope.vertices.push( vector );
+
+ }
+
+ }
+
+ for ( iy = 0; iy < gridY; iy ++ ) {
+
+ for ( ix = 0; ix < gridX; ix ++ ) {
+
+ var a = ix + gridX1 * iy;
+ var b = ix + gridX1 * ( iy + 1 );
+ var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
+ var d = ( ix + 1 ) + gridX1 * iy;
+
+ var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY );
+ var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY );
+ var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY );
+ var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY );
+
+ var face = new THREE.Face3( a + offset, b + offset, d + offset );
+ face.normal.copy( normal );
+ face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
+ face.materialIndex = materialIndex;
+
+ scope.faces.push( face );
+ scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
+
+ face = new THREE.Face3( b + offset, c + offset, d + offset );
+ face.normal.copy( normal );
+ face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
+ face.materialIndex = materialIndex;
+
+ scope.faces.push( face );
+ scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
+
+ }
+
+ }
+
+ }
+
+ this.mergeVertices();
+
+};
+
+THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.BoxGeometry.prototype.constructor = THREE.BoxGeometry;
+
+THREE.BoxGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.BoxGeometry(
+ parameters.width,
+ parameters.height,
+ parameters.depth,
+ parameters.widthSegments,
+ parameters.heightSegments,
+ parameters.depthSegments
+ );
+
+};
+
+THREE.CubeGeometry = THREE.BoxGeometry; // backwards compatibility
+
+// File:src/extras/geometries/CircleGeometry.js
+
+/**
+ * @author hughes
+ */
+
+THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'CircleGeometry';
+
+ this.parameters = {
+ radius: radius,
+ segments: segments,
+ thetaStart: thetaStart,
+ thetaLength: thetaLength
+ };
+
+ this.fromBufferGeometry( new THREE.CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) );
+
+};
+
+THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.CircleGeometry.prototype.constructor = THREE.CircleGeometry;
+
+THREE.CircleGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.CircleGeometry(
+ parameters.radius,
+ parameters.segments,
+ parameters.thetaStart,
+ parameters.thetaLength
+ );
+
+};
+
+// File:src/extras/geometries/CircleBufferGeometry.js
+
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.CircleBufferGeometry = function ( radius, segments, thetaStart, thetaLength ) {
+
+ THREE.BufferGeometry.call( this );
+
+ this.type = 'CircleBufferGeometry';
+
+ this.parameters = {
+ radius: radius,
+ segments: segments,
+ thetaStart: thetaStart,
+ thetaLength: thetaLength
+ };
+
+ radius = radius || 50;
+ segments = segments !== undefined ? Math.max( 3, segments ) : 8;
+
+ thetaStart = thetaStart !== undefined ? thetaStart : 0;
+ thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
+
+ var vertices = segments + 2;
+
+ var positions = new Float32Array( vertices * 3 );
+ var normals = new Float32Array( vertices * 3 );
+ var uvs = new Float32Array( vertices * 2 );
+
+ // center data is already zero, but need to set a few extras
+ normals[ 2 ] = 1.0;
+ uvs[ 0 ] = 0.5;
+ uvs[ 1 ] = 0.5;
+
+ for ( var s = 0, i = 3, ii = 2 ; s <= segments; s ++, i += 3, ii += 2 ) {
+
+ var segment = thetaStart + s / segments * thetaLength;
+
+ positions[ i ] = radius * Math.cos( segment );
+ positions[ i + 1 ] = radius * Math.sin( segment );
+
+ normals[ i + 2 ] = 1; // normal z
+
+ uvs[ ii ] = ( positions[ i ] / radius + 1 ) / 2;
+ uvs[ ii + 1 ] = ( positions[ i + 1 ] / radius + 1 ) / 2;
+
+ }
+
+ var indices = [];
+
+ for ( var i = 1; i <= segments; i ++ ) {
+
+ indices.push( i, i + 1, 0 );
+
+ }
+
+ this.setIndex( new THREE.BufferAttribute( new Uint16Array( indices ), 1 ) );
+ this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
+ this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
+ this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
+
+ this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+
+};
+
+THREE.CircleBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.CircleBufferGeometry.prototype.constructor = THREE.CircleBufferGeometry;
+
+THREE.CircleBufferGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.CircleBufferGeometry(
+ parameters.radius,
+ parameters.segments,
+ parameters.thetaStart,
+ parameters.thetaLength
+ );
+
+};
+
+// File:src/extras/geometries/CylinderGeometry.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'CylinderGeometry';
+
+ this.parameters = {
+ radiusTop: radiusTop,
+ radiusBottom: radiusBottom,
+ height: height,
+ radialSegments: radialSegments,
+ heightSegments: heightSegments,
+ openEnded: openEnded,
+ thetaStart: thetaStart,
+ thetaLength: thetaLength
+ };
+
+ radiusTop = radiusTop !== undefined ? radiusTop : 20;
+ radiusBottom = radiusBottom !== undefined ? radiusBottom : 20;
+ height = height !== undefined ? height : 100;
+
+ radialSegments = radialSegments || 8;
+ heightSegments = heightSegments || 1;
+
+ openEnded = openEnded !== undefined ? openEnded : false;
+ thetaStart = thetaStart !== undefined ? thetaStart : 0;
+ thetaLength = thetaLength !== undefined ? thetaLength : 2 * Math.PI;
+
+ var heightHalf = height / 2;
+
+ var x, y, vertices = [], uvs = [];
+
+ for ( y = 0; y <= heightSegments; y ++ ) {
+
+ var verticesRow = [];
+ var uvsRow = [];
+
+ var v = y / heightSegments;
+ var radius = v * ( radiusBottom - radiusTop ) + radiusTop;
+
+ for ( x = 0; x <= radialSegments; x ++ ) {
+
+ var u = x / radialSegments;
+
+ var vertex = new THREE.Vector3();
+ vertex.x = radius * Math.sin( u * thetaLength + thetaStart );
+ vertex.y = - v * height + heightHalf;
+ vertex.z = radius * Math.cos( u * thetaLength + thetaStart );
+
+ this.vertices.push( vertex );
+
+ verticesRow.push( this.vertices.length - 1 );
+ uvsRow.push( new THREE.Vector2( u, 1 - v ) );
+
+ }
+
+ vertices.push( verticesRow );
+ uvs.push( uvsRow );
+
+ }
+
+ var tanTheta = ( radiusBottom - radiusTop ) / height;
+ var na, nb;
+
+ for ( x = 0; x < radialSegments; x ++ ) {
+
+ if ( radiusTop !== 0 ) {
+
+ na = this.vertices[ vertices[ 0 ][ x ] ].clone();
+ nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone();
+
+ } else {
+
+ na = this.vertices[ vertices[ 1 ][ x ] ].clone();
+ nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone();
+
+ }
+
+ na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize();
+ nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize();
+
+ for ( y = 0; y < heightSegments; y ++ ) {
+
+ var v1 = vertices[ y ][ x ];
+ var v2 = vertices[ y + 1 ][ x ];
+ var v3 = vertices[ y + 1 ][ x + 1 ];
+ var v4 = vertices[ y ][ x + 1 ];
+
+ var n1 = na.clone();
+ var n2 = na.clone();
+ var n3 = nb.clone();
+ var n4 = nb.clone();
+
+ var uv1 = uvs[ y ][ x ].clone();
+ var uv2 = uvs[ y + 1 ][ x ].clone();
+ var uv3 = uvs[ y + 1 ][ x + 1 ].clone();
+ var uv4 = uvs[ y ][ x + 1 ].clone();
+
+ this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
+ this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );
+
+ this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) );
+ this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );
+
+ }
+
+ }
+
+ // top cap
+
+ if ( openEnded === false && radiusTop > 0 ) {
+
+ this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) );
+
+ for ( x = 0; x < radialSegments; x ++ ) {
+
+ var v1 = vertices[ 0 ][ x ];
+ var v2 = vertices[ 0 ][ x + 1 ];
+ var v3 = this.vertices.length - 1;
+
+ var n1 = new THREE.Vector3( 0, 1, 0 );
+ var n2 = new THREE.Vector3( 0, 1, 0 );
+ var n3 = new THREE.Vector3( 0, 1, 0 );
+
+ var uv1 = uvs[ 0 ][ x ].clone();
+ var uv2 = uvs[ 0 ][ x + 1 ].clone();
+ var uv3 = new THREE.Vector2( uv2.x, 0 );
+
+ this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ], undefined, 1 ) );
+ this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
+
+ }
+
+ }
+
+ // bottom cap
+
+ if ( openEnded === false && radiusBottom > 0 ) {
+
+ this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) );
+
+ for ( x = 0; x < radialSegments; x ++ ) {
+
+ var v1 = vertices[ heightSegments ][ x + 1 ];
+ var v2 = vertices[ heightSegments ][ x ];
+ var v3 = this.vertices.length - 1;
+
+ var n1 = new THREE.Vector3( 0, - 1, 0 );
+ var n2 = new THREE.Vector3( 0, - 1, 0 );
+ var n3 = new THREE.Vector3( 0, - 1, 0 );
+
+ var uv1 = uvs[ heightSegments ][ x + 1 ].clone();
+ var uv2 = uvs[ heightSegments ][ x ].clone();
+ var uv3 = new THREE.Vector2( uv2.x, 1 );
+
+ this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ], undefined, 2 ) );
+ this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
+
+ }
+
+ }
+
+ this.computeFaceNormals();
+
+};
+
+THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.CylinderGeometry.prototype.constructor = THREE.CylinderGeometry;
+
+THREE.CylinderGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.CylinderGeometry(
+ parameters.radiusTop,
+ parameters.radiusBottom,
+ parameters.height,
+ parameters.radialSegments,
+ parameters.heightSegments,
+ parameters.openEnded,
+ parameters.thetaStart,
+ parameters.thetaLength
+ );
+
+};
+
+// File:src/extras/geometries/EdgesGeometry.js
+
+/**
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.EdgesGeometry = function ( geometry, thresholdAngle ) {
+
+ THREE.BufferGeometry.call( this );
+
+ thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1;
+
+ var thresholdDot = Math.cos( THREE.Math.degToRad( thresholdAngle ) );
+
+ var edge = [ 0, 0 ], hash = {};
+
+ function sortFunction( a, b ) {
+
+ return a - b;
+
+ }
+
+ var keys = [ 'a', 'b', 'c' ];
+
+ var geometry2;
+
+ if ( geometry instanceof THREE.BufferGeometry ) {
+
+ geometry2 = new THREE.Geometry();
+ geometry2.fromBufferGeometry( geometry );
+
+ } else {
+
+ geometry2 = geometry.clone();
+
+ }
+
+ geometry2.mergeVertices();
+ geometry2.computeFaceNormals();
+
+ var vertices = geometry2.vertices;
+ var faces = geometry2.faces;
+
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
+
+ var face = faces[ i ];
+
+ for ( var j = 0; j < 3; j ++ ) {
+
+ edge[ 0 ] = face[ keys[ j ] ];
+ edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
+ edge.sort( sortFunction );
+
+ var key = edge.toString();
+
+ if ( hash[ key ] === undefined ) {
+
+ hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined };
+
+ } else {
+
+ hash[ key ].face2 = i;
+
+ }
+
+ }
+
+ }
+
+ var coords = [];
+
+ for ( var key in hash ) {
+
+ var h = hash[ key ];
+
+ if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) <= thresholdDot ) {
+
+ var vertex = vertices[ h.vert1 ];
+ coords.push( vertex.x );
+ coords.push( vertex.y );
+ coords.push( vertex.z );
+
+ vertex = vertices[ h.vert2 ];
+ coords.push( vertex.x );
+ coords.push( vertex.y );
+ coords.push( vertex.z );
+
+ }
+
+ }
+
+ this.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( coords ), 3 ) );
+
+};
+
+THREE.EdgesGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.EdgesGeometry.prototype.constructor = THREE.EdgesGeometry;
+
+// File:src/extras/geometries/ExtrudeGeometry.js
+
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ *
+ * Creates extruded geometry from a path shape.
+ *
+ * parameters = {
+ *
+ * curveSegments: , // number of points on the curves
+ * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too
+ * amount: , // Depth to extrude the shape
+ *
+ * bevelEnabled: , // turn on bevel
+ * bevelThickness: , // how deep into the original shape bevel goes
+ * bevelSize: , // how far from shape outline is bevel
+ * bevelSegments: , // number of bevel layers
+ *
+ * extrudePath: // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined)
+ * frames: // containing arrays of tangents, normals, binormals
+ *
+ * uvGenerator:
+ * Allows to compute the original undistorted radius from a distorted one.
+ * See also getApproximateInverseDistortion() for a faster but potentially
+ * less accurate method.
+ *
+ * @param {Number} radius Distorted radius from the lens center in tan-angle units.
+ * @return {Number} The undistorted radius in tan-angle units.
+ */
+Distortion.prototype.distortInverse = function(radius) {
+ // Secant method.
+ var r0 = radius / 0.9;
+ var r1 = radius * 0.9;
+ var dr0 = radius - this.distort(r0);
+ while (Math.abs(r1 - r0) > 0.0001 /** 0.1mm */) {
+ var dr1 = radius - this.distort(r1);
+ var r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0));
+ r0 = r1;
+ r1 = r2;
+ dr0 = dr1;
+ }
+ return r1;
+}
+
+
+/**
+ * Distorts a radius by its distortion factor from the center of the lenses.
+ *
+ * @param {Number} radius Radius from the lens center in tan-angle units.
+ * @return {Number} The distorted radius in tan-angle units.
+ */
+Distortion.prototype.distort = function(radius) {
+ return radius * this.distortionFactor_(radius);
+}
+
+/**
+ * Returns the distortion factor of a point.
+ *
+ * @param {Number} radius Radius of the point from the lens center in tan-angle units.
+ * @return {Number} The distortion factor. Multiply by this factor to distort points.
+ */
+Distortion.prototype.distortionFactor_ = function(radius) {
+ var result = 1.0;
+ var rFactor = 1.0;
+ var rSquared = radius * radius;
+
+ for (var i = 0; i < this.coefficients.length; i++) {
+ var ki = this.coefficients[i];
+ rFactor *= rSquared;
+ result += ki * rFactor;
+ }
+
+ return result;
+}
+
+module.exports = Distortion;
+
+},{}],7:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * DPDB cache.
+ */
+var DPDB_CACHE = {
+ "format": 1,
+ "last_updated": "2016-01-26T23:11:18Z",
+ "devices": [
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "asus/*/Nexus 7/*" },
+ { "ua": "Nexus 7" }
+ ],
+ "dpi": [ 320.8, 323.0 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "asus/*/ASUS_Z00AD/*" },
+ { "ua": "ASUS_Z00AD" }
+ ],
+ "dpi": [ 403.0, 404.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "HTC/*/HTC6435LVW/*" },
+ { "ua": "HTC6435LVW" }
+ ],
+ "dpi": [ 449.7, 443.3 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "HTC/*/HTC One XL/*" },
+ { "ua": "HTC One XL" }
+ ],
+ "dpi": [ 315.3, 314.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "htc/*/Nexus 9/*" },
+ { "ua": "Nexus 9" }
+ ],
+ "dpi": 289.0,
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "HTC/*/HTC One M9/*" },
+ { "ua": "HTC One M9" }
+ ],
+ "dpi": [ 442.5, 443.3 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "HTC/*/HTC One_M8/*" },
+ { "ua": "HTC One_M8" }
+ ],
+ "dpi": [ 449.7, 447.4 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "HTC/*/HTC One/*" },
+ { "ua": "HTC One" }
+ ],
+ "dpi": 472.8,
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Huawei/*/Nexus 6P/*" },
+ { "ua": "Nexus 6P" }
+ ],
+ "dpi": [ 515.1, 518.0 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/Nexus 5X/*" },
+ { "ua": "Nexus 5X" }
+ ],
+ "dpi": [ 422.0, 419.9 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/LGMS345/*" },
+ { "ua": "LGMS345" }
+ ],
+ "dpi": [ 221.7, 219.1 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/LG-D800/*" },
+ { "ua": "LG-D800" }
+ ],
+ "dpi": [ 422.0, 424.1 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/LG-D850/*" },
+ { "ua": "LG-D850" }
+ ],
+ "dpi": [ 537.9, 541.9 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/VS985 4G/*" },
+ { "ua": "VS985 4G" }
+ ],
+ "dpi": [ 537.9, 535.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/Nexus 5/*" },
+ { "ua": "Nexus 5 B" }
+ ],
+ "dpi": [ 442.4, 444.8 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/Nexus 4/*" },
+ { "ua": "Nexus 4" }
+ ],
+ "dpi": [ 319.8, 318.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/LG-P769/*" },
+ { "ua": "LG-P769" }
+ ],
+ "dpi": [ 240.6, 247.5 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/LGMS323/*" },
+ { "ua": "LGMS323" }
+ ],
+ "dpi": [ 206.6, 204.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/LGLS996/*" },
+ { "ua": "LGLS996" }
+ ],
+ "dpi": [ 403.4, 401.5 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Micromax/*/4560MMX/*" },
+ { "ua": "4560MMX" }
+ ],
+ "dpi": [ 240.0, 219.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Micromax/*/A250/*" },
+ { "ua": "Micromax A250" }
+ ],
+ "dpi": [ 480.0, 446.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Micromax/*/Micromax AQ4501/*" },
+ { "ua": "Micromax AQ4501" }
+ ],
+ "dpi": 240.0,
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/DROID RAZR/*" },
+ { "ua": "DROID RAZR" }
+ ],
+ "dpi": [ 368.1, 256.7 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT830C/*" },
+ { "ua": "XT830C" }
+ ],
+ "dpi": [ 254.0, 255.9 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1021/*" },
+ { "ua": "XT1021" }
+ ],
+ "dpi": [ 254.0, 256.7 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1023/*" },
+ { "ua": "XT1023" }
+ ],
+ "dpi": [ 254.0, 256.7 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1028/*" },
+ { "ua": "XT1028" }
+ ],
+ "dpi": [ 326.6, 327.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1034/*" },
+ { "ua": "XT1034" }
+ ],
+ "dpi": [ 326.6, 328.4 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1053/*" },
+ { "ua": "XT1053" }
+ ],
+ "dpi": [ 315.3, 316.1 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1562/*" },
+ { "ua": "XT1562" }
+ ],
+ "dpi": [ 403.4, 402.7 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/Nexus 6/*" },
+ { "ua": "Nexus 6 B" }
+ ],
+ "dpi": [ 494.3, 489.7 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1063/*" },
+ { "ua": "XT1063" }
+ ],
+ "dpi": [ 295.0, 296.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1064/*" },
+ { "ua": "XT1064" }
+ ],
+ "dpi": [ 295.0, 295.6 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1092/*" },
+ { "ua": "XT1092" }
+ ],
+ "dpi": [ 422.0, 424.1 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1095/*" },
+ { "ua": "XT1095" }
+ ],
+ "dpi": [ 422.0, 423.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "OnePlus/*/A0001/*" },
+ { "ua": "A0001" }
+ ],
+ "dpi": [ 403.4, 401.0 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "OnePlus/*/ONE E1005/*" },
+ { "ua": "ONE E1005" }
+ ],
+ "dpi": [ 442.4, 441.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "OnePlus/*/ONE A2005/*" },
+ { "ua": "ONE A2005" }
+ ],
+ "dpi": [ 391.9, 405.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "OPPO/*/X909/*" },
+ { "ua": "X909" }
+ ],
+ "dpi": [ 442.4, 444.1 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-I9082/*" },
+ { "ua": "GT-I9082" }
+ ],
+ "dpi": [ 184.7, 185.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G360P/*" },
+ { "ua": "SM-G360P" }
+ ],
+ "dpi": [ 196.7, 205.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/Nexus S/*" },
+ { "ua": "Nexus S" }
+ ],
+ "dpi": [ 234.5, 229.8 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-I9300/*" },
+ { "ua": "GT-I9300" }
+ ],
+ "dpi": [ 304.8, 303.9 ],
+ "bw": 5,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-T230NU/*" },
+ { "ua": "SM-T230NU" }
+ ],
+ "dpi": 216.0,
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SGH-T399/*" },
+ { "ua": "SGH-T399" }
+ ],
+ "dpi": [ 217.7, 231.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-N9005/*" },
+ { "ua": "SM-N9005" }
+ ],
+ "dpi": [ 386.4, 387.0 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SAMSUNG-SM-N900A/*" },
+ { "ua": "SAMSUNG-SM-N900A" }
+ ],
+ "dpi": [ 386.4, 387.7 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-I9500/*" },
+ { "ua": "GT-I9500" }
+ ],
+ "dpi": [ 442.5, 443.3 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-I9505/*" },
+ { "ua": "GT-I9505" }
+ ],
+ "dpi": 439.4,
+ "bw": 4,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G900F/*" },
+ { "ua": "SM-G900F" }
+ ],
+ "dpi": [ 415.6, 431.6 ],
+ "bw": 5,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G900M/*" },
+ { "ua": "SM-G900M" }
+ ],
+ "dpi": [ 415.6, 431.6 ],
+ "bw": 5,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G800F/*" },
+ { "ua": "SM-G800F" }
+ ],
+ "dpi": 326.8,
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G906S/*" },
+ { "ua": "SM-G906S" }
+ ],
+ "dpi": [ 562.7, 572.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-I9300/*" },
+ { "ua": "GT-I9300" }
+ ],
+ "dpi": [ 306.7, 304.8 ],
+ "bw": 5,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-T535/*" },
+ { "ua": "SM-T535" }
+ ],
+ "dpi": [ 142.6, 136.4 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-N920C/*" },
+ { "ua": "SM-N920C" }
+ ],
+ "dpi": [ 515.1, 518.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-I9300I/*" },
+ { "ua": "GT-I9300I" }
+ ],
+ "dpi": [ 304.8, 305.8 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-I9195/*" },
+ { "ua": "GT-I9195" }
+ ],
+ "dpi": [ 249.4, 256.7 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SPH-L520/*" },
+ { "ua": "SPH-L520" }
+ ],
+ "dpi": [ 249.4, 255.9 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SAMSUNG-SGH-I717/*" },
+ { "ua": "SAMSUNG-SGH-I717" }
+ ],
+ "dpi": 285.8,
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SPH-D710/*" },
+ { "ua": "SPH-D710" }
+ ],
+ "dpi": [ 217.7, 204.2 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-N7100/*" },
+ { "ua": "GT-N7100" }
+ ],
+ "dpi": 265.1,
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SCH-I605/*" },
+ { "ua": "SCH-I605" }
+ ],
+ "dpi": 265.1,
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/Galaxy Nexus/*" },
+ { "ua": "Galaxy Nexus" }
+ ],
+ "dpi": [ 315.3, 314.2 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-N910H/*" },
+ { "ua": "SM-N910H" }
+ ],
+ "dpi": [ 515.1, 518.0 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-N910C/*" },
+ { "ua": "SM-N910C" }
+ ],
+ "dpi": [ 515.2, 520.2 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G130M/*" },
+ { "ua": "SM-G130M" }
+ ],
+ "dpi": [ 165.9, 164.8 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G928I/*" },
+ { "ua": "SM-G928I" }
+ ],
+ "dpi": [ 515.1, 518.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G920F/*" },
+ { "ua": "SM-G920F" }
+ ],
+ "dpi": 580.6,
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G920P/*" },
+ { "ua": "SM-G920P" }
+ ],
+ "dpi": [ 522.5, 577.0 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G925F/*" },
+ { "ua": "SM-G925F" }
+ ],
+ "dpi": 580.6,
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G925V/*" },
+ { "ua": "SM-G925V" }
+ ],
+ "dpi": [ 522.5, 576.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Sony/*/C6903/*" },
+ { "ua": "C6903" }
+ ],
+ "dpi": [ 442.5, 443.3 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Sony/*/D6653/*" },
+ { "ua": "D6653" }
+ ],
+ "dpi": [ 428.6, 427.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Sony/*/E6653/*" },
+ { "ua": "E6653" }
+ ],
+ "dpi": [ 428.6, 425.7 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Sony/*/E6853/*" },
+ { "ua": "E6853" }
+ ],
+ "dpi": [ 403.4, 401.9 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Sony/*/SGP321/*" },
+ { "ua": "SGP321" }
+ ],
+ "dpi": [ 224.7, 224.1 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "TCT/*/ALCATEL ONE TOUCH Fierce/*" },
+ { "ua": "ALCATEL ONE TOUCH Fierce" }
+ ],
+ "dpi": [ 240.0, 247.5 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "THL/*/thl 5000/*" },
+ { "ua": "thl 5000" }
+ ],
+ "dpi": [ 480.0, 443.3 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "ZTE/*/ZTE Blade L2/*" },
+ { "ua": "ZTE Blade L2" }
+ ],
+ "dpi": 240.0,
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "ios",
+ "rules": [ { "res": [ 640, 960 ] } ],
+ "dpi": [ 325.1, 328.4 ],
+ "bw": 4,
+ "ac": 1000
+ },
+ {
+ "type": "ios",
+ "rules": [ { "res": [ 640, 1136 ] } ],
+ "dpi": [ 317.1, 320.2 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "ios",
+ "rules": [ { "res": [ 750, 1334 ] } ],
+ "dpi": 326.4,
+ "bw": 4,
+ "ac": 1000
+ },
+ {
+ "type": "ios",
+ "rules": [ { "res": [ 1242, 2208 ] } ],
+ "dpi": [ 453.6, 458.4 ],
+ "bw": 4,
+ "ac": 1000
+ },
+ {
+ "type": "ios",
+ "rules": [ { "res": [ 1125, 2001 ] } ],
+ "dpi": [ 410.9, 415.4 ],
+ "bw": 4,
+ "ac": 1000
+ }
+]};
+
+module.exports = DPDB_CACHE;
+
+
+},{}],8:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Offline cache of the DPDB, to be used until we load the online one (and
+// as a fallback in case we can't load the online one).
+var DPDB_CACHE = _dereq_('./dpdb-cache.js');
+var Util = _dereq_('./util.js');
+
+// Online DPDB URL.
+var ONLINE_DPDB_URL = 'https://storage.googleapis.com/cardboard-dpdb/dpdb.json';
+
+/**
+ * Calculates device parameters based on the DPDB (Device Parameter Database).
+ * Initially, uses the cached DPDB values.
+ *
+ * If fetchOnline == true, then this object tries to fetch the online version
+ * of the DPDB and updates the device info if a better match is found.
+ * Calls the onDeviceParamsUpdated callback when there is an update to the
+ * device information.
+ */
+function Dpdb(fetchOnline, onDeviceParamsUpdated) {
+ // Start with the offline DPDB cache while we are loading the real one.
+ this.dpdb = DPDB_CACHE;
+
+ // Calculate device params based on the offline version of the DPDB.
+ this.recalculateDeviceParams_();
+
+ // XHR to fetch online DPDB file, if requested.
+ if (fetchOnline) {
+ // Set the callback.
+ this.onDeviceParamsUpdated = onDeviceParamsUpdated;
+
+ console.log('Fetching DPDB...');
+ var xhr = new XMLHttpRequest();
+ var obj = this;
+ xhr.open('GET', ONLINE_DPDB_URL, true);
+ xhr.addEventListener('load', function() {
+ obj.loading = false;
+ if (xhr.status >= 200 && xhr.status <= 299) {
+ // Success.
+ console.log('Successfully loaded online DPDB.');
+ obj.dpdb = JSON.parse(xhr.response);
+ obj.recalculateDeviceParams_();
+ } else {
+ // Error loading the DPDB.
+ console.error('Error loading online DPDB!');
+ }
+ });
+ xhr.send();
+ }
+}
+
+// Returns the current device parameters.
+Dpdb.prototype.getDeviceParams = function() {
+ return this.deviceParams;
+};
+
+// Recalculates this device's parameters based on the DPDB.
+Dpdb.prototype.recalculateDeviceParams_ = function() {
+ console.log('Recalculating device params.');
+ var newDeviceParams = this.calcDeviceParams_();
+ console.log('New device parameters:');
+ console.log(newDeviceParams);
+ if (newDeviceParams) {
+ this.deviceParams = newDeviceParams;
+ // Invoke callback, if it is set.
+ if (this.onDeviceParamsUpdated) {
+ this.onDeviceParamsUpdated(this.deviceParams);
+ }
+ } else {
+ console.warn('Failed to recalculate device parameters.');
+ }
+};
+
+// Returns a DeviceParams object that represents the best guess as to this
+// device's parameters. Can return null if the device does not match any
+// known devices.
+Dpdb.prototype.calcDeviceParams_ = function() {
+ var db = this.dpdb; // shorthand
+ if (!db) {
+ console.error('DPDB not available.');
+ return null;
+ }
+ if (db.format != 1) {
+ console.error('DPDB has unexpected format version.');
+ return null;
+ }
+ if (!db.devices || !db.devices.length) {
+ console.error('DPDB does not have a devices section.');
+ return null;
+ }
+
+ // Get the actual user agent and screen dimensions in pixels.
+ var userAgent = navigator.userAgent || navigator.vendor || window.opera;
+ var width = Util.getScreenWidth();
+ var height = Util.getScreenHeight();
+ console.log('User agent: ' + userAgent);
+ console.log('Pixel width: ' + width);
+ console.log('Pixel height: ' + height);
+
+ if (!db.devices) {
+ console.error('DPDB has no devices section.');
+ return null;
+ }
+
+ for (var i = 0; i < db.devices.length; i++) {
+ var device = db.devices[i];
+ if (!device.rules) {
+ console.warn('Device[' + i + '] has no rules section.');
+ continue;
+ }
+
+ if (device.type != 'ios' && device.type != 'android') {
+ console.warn('Device[' + i + '] has invalid type.');
+ continue;
+ }
+
+ // See if this device is of the appropriate type.
+ if (Util.isIOS() != (device.type == 'ios')) continue;
+
+ // See if this device matches any of the rules:
+ var matched = false;
+ for (var j = 0; j < device.rules.length; j++) {
+ var rule = device.rules[j];
+ if (this.matchRule_(rule, userAgent, width, height)) {
+ console.log('Rule matched:');
+ console.log(rule);
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) continue;
+
+ // device.dpi might be an array of [ xdpi, ydpi] or just a scalar.
+ var xdpi = device.dpi[0] || device.dpi;
+ var ydpi = device.dpi[1] || device.dpi;
+
+ return new DeviceParams({ xdpi: xdpi, ydpi: ydpi, bevelMm: device.bw });
+ }
+
+ console.warn('No DPDB device match.');
+ return null;
+};
+
+Dpdb.prototype.matchRule_ = function(rule, ua, screenWidth, screenHeight) {
+ // We can only match 'ua' and 'res' rules, not other types like 'mdmh'
+ // (which are meant for native platforms).
+ if (!rule.ua && !rule.res) return false;
+
+ // If our user agent string doesn't contain the indicated user agent string,
+ // the match fails.
+ if (rule.ua && ua.indexOf(rule.ua) < 0) return false;
+
+ // If the rule specifies screen dimensions that don't correspond to ours,
+ // the match fails.
+ if (rule.res) {
+ if (!rule.res[0] || !rule.res[1]) return false;
+ var resX = rule.res[0];
+ var resY = rule.res[1];
+ // Compare min and max so as to make the order not matter, i.e., it should
+ // be true that 640x480 == 480x640.
+ if (Math.min(screenWidth, screenHeight) != Math.min(resX, resY) ||
+ (Math.max(screenWidth, screenHeight) != Math.max(resX, resY))) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+function DeviceParams(params) {
+ this.xdpi = params.xdpi;
+ this.ydpi = params.ydpi;
+ this.bevelMm = params.bevelMm;
+}
+
+module.exports = Dpdb;
+
+},{"./dpdb-cache.js":7,"./util.js":13}],9:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+function Emitter() {
+ this.callbacks = {};
+}
+
+Emitter.prototype.emit = function(eventName) {
+ var callbacks = this.callbacks[eventName];
+ if (!callbacks) {
+ //console.log('No valid callback specified.');
+ return;
+ }
+ var args = [].slice.call(arguments);
+ // Eliminate the first param (the callback).
+ args.shift();
+ for (var i = 0; i < callbacks.length; i++) {
+ callbacks[i].apply(this, args);
+ }
+};
+
+Emitter.prototype.on = function(eventName, callback) {
+ if (eventName in this.callbacks) {
+ this.callbacks[eventName].push(callback);
+ } else {
+ this.callbacks[eventName] = [callback];
+ }
+};
+
+module.exports = Emitter;
+
+},{}],10:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var WebVRManager = _dereq_('./webvr-manager.js');
+
+window.WebVRConfig = window.WebVRConfig || {};
+window.WebVRManager = WebVRManager;
+
+},{"./webvr-manager.js":16}],11:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var Modes = {
+ UNKNOWN: 0,
+ // Not fullscreen, just tracking.
+ NORMAL: 1,
+ // Magic window immersive mode.
+ MAGIC_WINDOW: 2,
+ // Full screen split screen VR mode.
+ VR: 3,
+};
+
+module.exports = Modes;
+
+},{}],12:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var Util = _dereq_('./util.js');
+
+function RotateInstructions() {
+ this.loadIcon_();
+
+ var overlay = document.createElement('div');
+ var s = overlay.style;
+ s.position = 'fixed';
+ s.top = 0;
+ s.right = 0;
+ s.bottom = 0;
+ s.left = 0;
+ s.backgroundColor = 'gray';
+ s.fontFamily = 'sans-serif';
+
+ var img = document.createElement('img');
+ img.src = this.icon;
+ var s = img.style;
+ s.marginLeft = '25%';
+ s.marginTop = '25%';
+ s.width = '50%';
+ overlay.appendChild(img);
+
+ var text = document.createElement('div');
+ var s = text.style;
+ s.textAlign = 'center';
+ s.fontSize = '16px';
+ s.lineHeight = '24px';
+ s.margin = '24px 25%';
+ s.width = '50%';
+ text.innerHTML = 'Place your phone into your Cardboard viewer.';
+ overlay.appendChild(text);
+
+ var snackbar = document.createElement('div');
+ var s = snackbar.style;
+ s.backgroundColor = '#CFD8DC';
+ s.position = 'fixed';
+ s.bottom = 0;
+ s.width = '100%';
+ s.height = '48px';
+ s.padding = '14px 24px';
+ s.boxSizing = 'border-box';
+ s.color = '#656A6B';
+ overlay.appendChild(snackbar);
+
+ var snackbarText = document.createElement('div');
+ snackbarText.style.float = 'left';
+ snackbarText.innerHTML = 'No Cardboard viewer?';
+
+ var snackbarButton = document.createElement('a');
+ snackbarButton.href = 'https://www.google.com/get/cardboard/get-cardboard/';
+ snackbarButton.innerHTML = 'get one';
+ snackbarButton.target = '_blank';
+ var s = snackbarButton.style;
+ s.float = 'right';
+ s.fontWeight = 600;
+ s.textTransform = 'uppercase';
+ s.borderLeft = '1px solid gray';
+ s.paddingLeft = '24px';
+ s.textDecoration = 'none';
+ s.color = '#656A6B';
+
+ snackbar.appendChild(snackbarText);
+ snackbar.appendChild(snackbarButton);
+
+ this.overlay = overlay;
+ this.text = text;
+ document.body.appendChild(overlay);
+
+ this.hide();
+}
+
+RotateInstructions.prototype.show = function() {
+ this.overlay.style.display = 'block';
+
+ var img = this.overlay.querySelector('img');
+ var s = img.style;
+
+ if (Util.isLandscapeMode()) {
+ s.width = '20%';
+ s.marginLeft = '40%';
+ s.marginTop = '3%';
+ } else {
+ s.width = '50%';
+ s.marginLeft = '25%';
+ s.marginTop = '25%';
+ }
+};
+
+RotateInstructions.prototype.hide = function() {
+ this.overlay.style.display = 'none';
+};
+
+RotateInstructions.prototype.showTemporarily = function(ms) {
+ this.show();
+ this.timer = setTimeout(this.hide.bind(this), ms);
+};
+
+RotateInstructions.prototype.disableShowTemporarily = function() {
+ clearTimeout(this.timer);
+};
+
+RotateInstructions.prototype.loadIcon_ = function() {
+ // Encoded asset_src/rotate-instructions.svg
+ this.icon = Util.base64('image/svg+xml', 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjE5OHB4IiBoZWlnaHQ9IjI0MHB4IiB2aWV3Qm94PSIwIDAgMTk4IDI0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpza2V0Y2g9Imh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaC9ucyI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDMuMy4zICgxMjA4MSkgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+dHJhbnNpdGlvbjwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPjwvZGVmcz4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHNrZXRjaDp0eXBlPSJNU1BhZ2UiPgogICAgICAgIDxnIGlkPSJ0cmFuc2l0aW9uIiBza2V0Y2g6dHlwZT0iTVNBcnRib2FyZEdyb3VwIj4KICAgICAgICAgICAgPGcgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTQtKy1JbXBvcnRlZC1MYXllcnMtQ29weS0rLUltcG9ydGVkLUxheWVycy1Db3B5LTItQ29weSIgc2tldGNoOnR5cGU9Ik1TTGF5ZXJHcm91cCI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHktNCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEwNy4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjYyNSwyLjUyNyBDMTQ5LjYyNSwyLjUyNyAxNTUuODA1LDYuMDk2IDE1Ni4zNjIsNi40MTggTDE1Ni4zNjIsNy4zMDQgQzE1Ni4zNjIsNy40ODEgMTU2LjM3NSw3LjY2NCAxNTYuNCw3Ljg1MyBDMTU2LjQxLDcuOTM0IDE1Ni40Miw4LjAxNSAxNTYuNDI3LDguMDk1IEMxNTYuNTY3LDkuNTEgMTU3LjQwMSwxMS4wOTMgMTU4LjUzMiwxMi4wOTQgTDE2NC4yNTIsMTcuMTU2IEwxNjQuMzMzLDE3LjA2NiBDMTY0LjMzMywxNy4wNjYgMTY4LjcxNSwxNC41MzYgMTY5LjU2OCwxNC4wNDIgQzE3MS4wMjUsMTQuODgzIDE5NS41MzgsMjkuMDM1IDE5NS41MzgsMjkuMDM1IEwxOTUuNTM4LDgzLjAzNiBDMTk1LjUzOCw4My44MDcgMTk1LjE1Miw4NC4yNTMgMTk0LjU5LDg0LjI1MyBDMTk0LjM1Nyw4NC4yNTMgMTk0LjA5NSw4NC4xNzcgMTkzLjgxOCw4NC4wMTcgTDE2OS44NTEsNzAuMTc5IEwxNjkuODM3LDcwLjIwMyBMMTQyLjUxNSw4NS45NzggTDE0MS42NjUsODQuNjU1IEMxMzYuOTM0LDgzLjEyNiAxMzEuOTE3LDgxLjkxNSAxMjYuNzE0LDgxLjA0NSBDMTI2LjcwOSw4MS4wNiAxMjYuNzA3LDgxLjA2OSAxMjYuNzA3LDgxLjA2OSBMMTIxLjY0LDk4LjAzIEwxMTMuNzQ5LDEwMi41ODYgTDExMy43MTIsMTAyLjUyMyBMMTEzLjcxMiwxMzAuMTEzIEMxMTMuNzEyLDEzMC44ODUgMTEzLjMyNiwxMzEuMzMgMTEyLjc2NCwxMzEuMzMgQzExMi41MzIsMTMxLjMzIDExMi4yNjksMTMxLjI1NCAxMTEuOTkyLDEzMS4wOTQgTDY5LjUxOSwxMDYuNTcyIEM2OC41NjksMTA2LjAyMyA2Ny43OTksMTA0LjY5NSA2Ny43OTksMTAzLjYwNSBMNjcuNzk5LDEwMi41NyBMNjcuNzc4LDEwMi42MTcgQzY3LjI3LDEwMi4zOTMgNjYuNjQ4LDEwMi4yNDkgNjUuOTYyLDEwMi4yMTggQzY1Ljg3NSwxMDIuMjE0IDY1Ljc4OCwxMDIuMjEyIDY1LjcwMSwxMDIuMjEyIEM2NS42MDYsMTAyLjIxMiA2NS41MTEsMTAyLjIxNSA2NS40MTYsMTAyLjIxOSBDNjUuMTk1LDEwMi4yMjkgNjQuOTc0LDEwMi4yMzUgNjQuNzU0LDEwMi4yMzUgQzY0LjMzMSwxMDIuMjM1IDYzLjkxMSwxMDIuMjE2IDYzLjQ5OCwxMDIuMTc4IEM2MS44NDMsMTAyLjAyNSA2MC4yOTgsMTAxLjU3OCA1OS4wOTQsMTAwLjg4MiBMMTIuNTE4LDczLjk5MiBMMTIuNTIzLDc0LjAwNCBMMi4yNDUsNTUuMjU0IEMxLjI0NCw1My40MjcgMi4wMDQsNTEuMDM4IDMuOTQzLDQ5LjkxOCBMNTkuOTU0LDE3LjU3MyBDNjAuNjI2LDE3LjE4NSA2MS4zNSwxNy4wMDEgNjIuMDUzLDE3LjAwMSBDNjMuMzc5LDE3LjAwMSA2NC42MjUsMTcuNjYgNjUuMjgsMTguODU0IEw2NS4yODUsMTguODUxIEw2NS41MTIsMTkuMjY0IEw2NS41MDYsMTkuMjY4IEM2NS45MDksMjAuMDAzIDY2LjQwNSwyMC42OCA2Ni45ODMsMjEuMjg2IEw2Ny4yNiwyMS41NTYgQzY5LjE3NCwyMy40MDYgNzEuNzI4LDI0LjM1NyA3NC4zNzMsMjQuMzU3IEM3Ni4zMjIsMjQuMzU3IDc4LjMyMSwyMy44NCA4MC4xNDgsMjIuNzg1IEM4MC4xNjEsMjIuNzg1IDg3LjQ2NywxOC41NjYgODcuNDY3LDE4LjU2NiBDODguMTM5LDE4LjE3OCA4OC44NjMsMTcuOTk0IDg5LjU2NiwxNy45OTQgQzkwLjg5MiwxNy45OTQgOTIuMTM4LDE4LjY1MiA5Mi43OTIsMTkuODQ3IEw5Ni4wNDIsMjUuNzc1IEw5Ni4wNjQsMjUuNzU3IEwxMDIuODQ5LDI5LjY3NCBMMTAyLjc0NCwyOS40OTIgTDE0OS42MjUsMi41MjcgTTE0OS42MjUsMC44OTIgQzE0OS4zNDMsMC44OTIgMTQ5LjA2MiwwLjk2NSAxNDguODEsMS4xMSBMMTAyLjY0MSwyNy42NjYgTDk3LjIzMSwyNC41NDIgTDk0LjIyNiwxOS4wNjEgQzkzLjMxMywxNy4zOTQgOTEuNTI3LDE2LjM1OSA4OS41NjYsMTYuMzU4IEM4OC41NTUsMTYuMzU4IDg3LjU0NiwxNi42MzIgODYuNjQ5LDE3LjE1IEM4My44NzgsMTguNzUgNzkuNjg3LDIxLjE2OSA3OS4zNzQsMjEuMzQ1IEM3OS4zNTksMjEuMzUzIDc5LjM0NSwyMS4zNjEgNzkuMzMsMjEuMzY5IEM3Ny43OTgsMjIuMjU0IDc2LjA4NCwyMi43MjIgNzQuMzczLDIyLjcyMiBDNzIuMDgxLDIyLjcyMiA2OS45NTksMjEuODkgNjguMzk3LDIwLjM4IEw2OC4xNDUsMjAuMTM1IEM2Ny43MDYsMTkuNjcyIDY3LjMyMywxOS4xNTYgNjcuMDA2LDE4LjYwMSBDNjYuOTg4LDE4LjU1OSA2Ni45NjgsMTguNTE5IDY2Ljk0NiwxOC40NzkgTDY2LjcxOSwxOC4wNjUgQzY2LjY5LDE4LjAxMiA2Ni42NTgsMTcuOTYgNjYuNjI0LDE3LjkxMSBDNjUuNjg2LDE2LjMzNyA2My45NTEsMTUuMzY2IDYyLjA1MywxNS4zNjYgQzYxLjA0MiwxNS4zNjYgNjAuMDMzLDE1LjY0IDU5LjEzNiwxNi4xNTggTDMuMTI1LDQ4LjUwMiBDMC40MjYsNTAuMDYxIC0wLjYxMyw1My40NDIgMC44MTEsNTYuMDQgTDExLjA4OSw3NC43OSBDMTEuMjY2LDc1LjExMyAxMS41MzcsNzUuMzUzIDExLjg1LDc1LjQ5NCBMNTguMjc2LDEwMi4yOTggQzU5LjY3OSwxMDMuMTA4IDYxLjQzMywxMDMuNjMgNjMuMzQ4LDEwMy44MDYgQzYzLjgxMiwxMDMuODQ4IDY0LjI4NSwxMDMuODcgNjQuNzU0LDEwMy44NyBDNjUsMTAzLjg3IDY1LjI0OSwxMDMuODY0IDY1LjQ5NCwxMDMuODUyIEM2NS41NjMsMTAzLjg0OSA2NS42MzIsMTAzLjg0NyA2NS43MDEsMTAzLjg0NyBDNjUuNzY0LDEwMy44NDcgNjUuODI4LDEwMy44NDkgNjUuODksMTAzLjg1MiBDNjUuOTg2LDEwMy44NTYgNjYuMDgsMTAzLjg2MyA2Ni4xNzMsMTAzLjg3NCBDNjYuMjgyLDEwNS40NjcgNjcuMzMyLDEwNy4xOTcgNjguNzAyLDEwNy45ODggTDExMS4xNzQsMTMyLjUxIEMxMTEuNjk4LDEzMi44MTIgMTEyLjIzMiwxMzIuOTY1IDExMi43NjQsMTMyLjk2NSBDMTE0LjI2MSwxMzIuOTY1IDExNS4zNDcsMTMxLjc2NSAxMTUuMzQ3LDEzMC4xMTMgTDExNS4zNDcsMTAzLjU1MSBMMTIyLjQ1OCw5OS40NDYgQzEyMi44MTksOTkuMjM3IDEyMy4wODcsOTguODk4IDEyMy4yMDcsOTguNDk4IEwxMjcuODY1LDgyLjkwNSBDMTMyLjI3OSw4My43MDIgMTM2LjU1Nyw4NC43NTMgMTQwLjYwNyw4Ni4wMzMgTDE0MS4xNCw4Ni44NjIgQzE0MS40NTEsODcuMzQ2IDE0MS45NzcsODcuNjEzIDE0Mi41MTYsODcuNjEzIEMxNDIuNzk0LDg3LjYxMyAxNDMuMDc2LDg3LjU0MiAxNDMuMzMzLDg3LjM5MyBMMTY5Ljg2NSw3Mi4wNzYgTDE5Myw4NS40MzMgQzE5My41MjMsODUuNzM1IDE5NC4wNTgsODUuODg4IDE5NC41OSw4NS44ODggQzE5Ni4wODcsODUuODg4IDE5Ny4xNzMsODQuNjg5IDE5Ny4xNzMsODMuMDM2IEwxOTcuMTczLDI5LjAzNSBDMTk3LjE3MywyOC40NTEgMTk2Ljg2MSwyNy45MTEgMTk2LjM1NSwyNy42MTkgQzE5Ni4zNTUsMjcuNjE5IDE3MS44NDMsMTMuNDY3IDE3MC4zODUsMTIuNjI2IEMxNzAuMTMyLDEyLjQ4IDE2OS44NSwxMi40MDcgMTY5LjU2OCwxMi40MDcgQzE2OS4yODUsMTIuNDA3IDE2OS4wMDIsMTIuNDgxIDE2OC43NDksMTIuNjI3IEMxNjguMTQzLDEyLjk3OCAxNjUuNzU2LDE0LjM1NyAxNjQuNDI0LDE1LjEyNSBMMTU5LjYxNSwxMC44NyBDMTU4Ljc5NiwxMC4xNDUgMTU4LjE1NCw4LjkzNyAxNTguMDU0LDcuOTM0IEMxNTguMDQ1LDcuODM3IDE1OC4wMzQsNy43MzkgMTU4LjAyMSw3LjY0IEMxNTguMDA1LDcuNTIzIDE1Ny45OTgsNy40MSAxNTcuOTk4LDcuMzA0IEwxNTcuOTk4LDYuNDE4IEMxNTcuOTk4LDUuODM0IDE1Ny42ODYsNS4yOTUgMTU3LjE4MSw1LjAwMiBDMTU2LjYyNCw0LjY4IDE1MC40NDIsMS4xMTEgMTUwLjQ0MiwxLjExMSBDMTUwLjE4OSwwLjk2NSAxNDkuOTA3LDAuODkyIDE0OS42MjUsMC44OTIiIGlkPSJGaWxsLTEiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTYuMDI3LDI1LjYzNiBMMTQyLjYwMyw1Mi41MjcgQzE0My44MDcsNTMuMjIyIDE0NC41ODIsNTQuMTE0IDE0NC44NDUsNTUuMDY4IEwxNDQuODM1LDU1LjA3NSBMNjMuNDYxLDEwMi4wNTcgTDYzLjQ2LDEwMi4wNTcgQzYxLjgwNiwxMDEuOTA1IDYwLjI2MSwxMDEuNDU3IDU5LjA1NywxMDAuNzYyIEwxMi40ODEsNzMuODcxIEw5Ni4wMjcsMjUuNjM2IiBpZD0iRmlsbC0yIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYzLjQ2MSwxMDIuMTc0IEM2My40NTMsMTAyLjE3NCA2My40NDYsMTAyLjE3NCA2My40MzksMTAyLjE3MiBDNjEuNzQ2LDEwMi4wMTYgNjAuMjExLDEwMS41NjMgNTguOTk4LDEwMC44NjMgTDEyLjQyMiw3My45NzMgQzEyLjM4Niw3My45NTIgMTIuMzY0LDczLjkxNCAxMi4zNjQsNzMuODcxIEMxMi4zNjQsNzMuODMgMTIuMzg2LDczLjc5MSAxMi40MjIsNzMuNzcgTDk1Ljk2OCwyNS41MzUgQzk2LjAwNCwyNS41MTQgOTYuMDQ5LDI1LjUxNCA5Ni4wODUsMjUuNTM1IEwxNDIuNjYxLDUyLjQyNiBDMTQzLjg4OCw1My4xMzQgMTQ0LjY4Miw1NC4wMzggMTQ0Ljk1Nyw1NS4wMzcgQzE0NC45Nyw1NS4wODMgMTQ0Ljk1Myw1NS4xMzMgMTQ0LjkxNSw1NS4xNjEgQzE0NC45MTEsNTUuMTY1IDE0NC44OTgsNTUuMTc0IDE0NC44OTQsNTUuMTc3IEw2My41MTksMTAyLjE1OCBDNjMuNTAxLDEwMi4xNjkgNjMuNDgxLDEwMi4xNzQgNjMuNDYxLDEwMi4xNzQgTDYzLjQ2MSwxMDIuMTc0IFogTTEyLjcxNCw3My44NzEgTDU5LjExNSwxMDAuNjYxIEM2MC4yOTMsMTAxLjM0MSA2MS43ODYsMTAxLjc4MiA2My40MzUsMTAxLjkzNyBMMTQ0LjcwNyw1NS4wMTUgQzE0NC40MjgsNTQuMTA4IDE0My42ODIsNTMuMjg1IDE0Mi41NDQsNTIuNjI4IEw5Ni4wMjcsMjUuNzcxIEwxMi43MTQsNzMuODcxIEwxMi43MTQsNzMuODcxIFoiIGlkPSJGaWxsLTMiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ4LjMyNyw1OC40NzEgQzE0OC4xNDUsNTguNDggMTQ3Ljk2Miw1OC40OCAxNDcuNzgxLDU4LjQ3MiBDMTQ1Ljg4Nyw1OC4zODkgMTQ0LjQ3OSw1Ny40MzQgMTQ0LjYzNiw1Ni4zNCBDMTQ0LjY4OSw1NS45NjcgMTQ0LjY2NCw1NS41OTcgMTQ0LjU2NCw1NS4yMzUgTDYzLjQ2MSwxMDIuMDU3IEM2NC4wODksMTAyLjExNSA2NC43MzMsMTAyLjEzIDY1LjM3OSwxMDIuMDk5IEM2NS41NjEsMTAyLjA5IDY1Ljc0MywxMDIuMDkgNjUuOTI1LDEwMi4wOTggQzY3LjgxOSwxMDIuMTgxIDY5LjIyNywxMDMuMTM2IDY5LjA3LDEwNC4yMyBMMTQ4LjMyNyw1OC40NzEiIGlkPSJGaWxsLTQiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNjkuMDcsMTA0LjM0NyBDNjkuMDQ4LDEwNC4zNDcgNjkuMDI1LDEwNC4zNCA2OS4wMDUsMTA0LjMyNyBDNjguOTY4LDEwNC4zMDEgNjguOTQ4LDEwNC4yNTcgNjguOTU1LDEwNC4yMTMgQzY5LDEwMy44OTYgNjguODk4LDEwMy41NzYgNjguNjU4LDEwMy4yODggQzY4LjE1MywxMDIuNjc4IDY3LjEwMywxMDIuMjY2IDY1LjkyLDEwMi4yMTQgQzY1Ljc0MiwxMDIuMjA2IDY1LjU2MywxMDIuMjA3IDY1LjM4NSwxMDIuMjE1IEM2NC43NDIsMTAyLjI0NiA2NC4wODcsMTAyLjIzMiA2My40NSwxMDIuMTc0IEM2My4zOTksMTAyLjE2OSA2My4zNTgsMTAyLjEzMiA2My4zNDcsMTAyLjA4MiBDNjMuMzM2LDEwMi4wMzMgNjMuMzU4LDEwMS45ODEgNjMuNDAyLDEwMS45NTYgTDE0NC41MDYsNTUuMTM0IEMxNDQuNTM3LDU1LjExNiAxNDQuNTc1LDU1LjExMyAxNDQuNjA5LDU1LjEyNyBDMTQ0LjY0Miw1NS4xNDEgMTQ0LjY2OCw1NS4xNyAxNDQuNjc3LDU1LjIwNCBDMTQ0Ljc4MSw1NS41ODUgMTQ0LjgwNiw1NS45NzIgMTQ0Ljc1MSw1Ni4zNTcgQzE0NC43MDYsNTYuNjczIDE0NC44MDgsNTYuOTk0IDE0NS4wNDcsNTcuMjgyIEMxNDUuNTUzLDU3Ljg5MiAxNDYuNjAyLDU4LjMwMyAxNDcuNzg2LDU4LjM1NSBDMTQ3Ljk2NCw1OC4zNjMgMTQ4LjE0Myw1OC4zNjMgMTQ4LjMyMSw1OC4zNTQgQzE0OC4zNzcsNTguMzUyIDE0OC40MjQsNTguMzg3IDE0OC40MzksNTguNDM4IEMxNDguNDU0LDU4LjQ5IDE0OC40MzIsNTguNTQ1IDE0OC4zODUsNTguNTcyIEw2OS4xMjksMTA0LjMzMSBDNjkuMTExLDEwNC4zNDIgNjkuMDksMTA0LjM0NyA2OS4wNywxMDQuMzQ3IEw2OS4wNywxMDQuMzQ3IFogTTY1LjY2NSwxMDEuOTc1IEM2NS43NTQsMTAxLjk3NSA2NS44NDIsMTAxLjk3NyA2NS45MywxMDEuOTgxIEM2Ny4xOTYsMTAyLjAzNyA2OC4yODMsMTAyLjQ2OSA2OC44MzgsMTAzLjEzOSBDNjkuMDY1LDEwMy40MTMgNjkuMTg4LDEwMy43MTQgNjkuMTk4LDEwNC4wMjEgTDE0Ny44ODMsNTguNTkyIEMxNDcuODQ3LDU4LjU5MiAxNDcuODExLDU4LjU5MSAxNDcuNzc2LDU4LjU4OSBDMTQ2LjUwOSw1OC41MzMgMTQ1LjQyMiw1OC4xIDE0NC44NjcsNTcuNDMxIEMxNDQuNTg1LDU3LjA5MSAxNDQuNDY1LDU2LjcwNyAxNDQuNTIsNTYuMzI0IEMxNDQuNTYzLDU2LjAyMSAxNDQuNTUyLDU1LjcxNiAxNDQuNDg4LDU1LjQxNCBMNjMuODQ2LDEwMS45NyBDNjQuMzUzLDEwMi4wMDIgNjQuODY3LDEwMi4wMDYgNjUuMzc0LDEwMS45ODIgQzY1LjQ3MSwxMDEuOTc3IDY1LjU2OCwxMDEuOTc1IDY1LjY2NSwxMDEuOTc1IEw2NS42NjUsMTAxLjk3NSBaIiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTIuMjA4LDU1LjEzNCBDMS4yMDcsNTMuMzA3IDEuOTY3LDUwLjkxNyAzLjkwNiw0OS43OTcgTDU5LjkxNywxNy40NTMgQzYxLjg1NiwxNi4zMzMgNjQuMjQxLDE2LjkwNyA2NS4yNDMsMTguNzM0IEw2NS40NzUsMTkuMTQ0IEM2NS44NzIsMTkuODgyIDY2LjM2OCwyMC41NiA2Ni45NDUsMjEuMTY1IEw2Ny4yMjMsMjEuNDM1IEM3MC41NDgsMjQuNjQ5IDc1LjgwNiwyNS4xNTEgODAuMTExLDIyLjY2NSBMODcuNDMsMTguNDQ1IEM4OS4zNywxNy4zMjYgOTEuNzU0LDE3Ljg5OSA5Mi43NTUsMTkuNzI3IEw5Ni4wMDUsMjUuNjU1IEwxMi40ODYsNzMuODg0IEwyLjIwOCw1NS4xMzQgWiIgaWQ9IkZpbGwtNiIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMi40ODYsNzQuMDAxIEMxMi40NzYsNzQuMDAxIDEyLjQ2NSw3My45OTkgMTIuNDU1LDczLjk5NiBDMTIuNDI0LDczLjk4OCAxMi4zOTksNzMuOTY3IDEyLjM4NCw3My45NCBMMi4xMDYsNTUuMTkgQzEuMDc1LDUzLjMxIDEuODU3LDUwLjg0NSAzLjg0OCw0OS42OTYgTDU5Ljg1OCwxNy4zNTIgQzYwLjUyNSwxNi45NjcgNjEuMjcxLDE2Ljc2NCA2Mi4wMTYsMTYuNzY0IEM2My40MzEsMTYuNzY0IDY0LjY2NiwxNy40NjYgNjUuMzI3LDE4LjY0NiBDNjUuMzM3LDE4LjY1NCA2NS4zNDUsMTguNjYzIDY1LjM1MSwxOC42NzQgTDY1LjU3OCwxOS4wODggQzY1LjU4NCwxOS4xIDY1LjU4OSwxOS4xMTIgNjUuNTkxLDE5LjEyNiBDNjUuOTg1LDE5LjgzOCA2Ni40NjksMjAuNDk3IDY3LjAzLDIxLjA4NSBMNjcuMzA1LDIxLjM1MSBDNjkuMTUxLDIzLjEzNyA3MS42NDksMjQuMTIgNzQuMzM2LDI0LjEyIEM3Ni4zMTMsMjQuMTIgNzguMjksMjMuNTgyIDgwLjA1MywyMi41NjMgQzgwLjA2NCwyMi41NTcgODAuMDc2LDIyLjU1MyA4MC4wODgsMjIuNTUgTDg3LjM3MiwxOC4zNDQgQzg4LjAzOCwxNy45NTkgODguNzg0LDE3Ljc1NiA4OS41MjksMTcuNzU2IEM5MC45NTYsMTcuNzU2IDkyLjIwMSwxOC40NzIgOTIuODU4LDE5LjY3IEw5Ni4xMDcsMjUuNTk5IEM5Ni4xMzgsMjUuNjU0IDk2LjExOCwyNS43MjQgOTYuMDYzLDI1Ljc1NiBMMTIuNTQ1LDczLjk4NSBDMTIuNTI2LDczLjk5NiAxMi41MDYsNzQuMDAxIDEyLjQ4Niw3NC4wMDEgTDEyLjQ4Niw3NC4wMDEgWiBNNjIuMDE2LDE2Ljk5NyBDNjEuMzEyLDE2Ljk5NyA2MC42MDYsMTcuMTkgNTkuOTc1LDE3LjU1NCBMMy45NjUsNDkuODk5IEMyLjA4Myw1MC45ODUgMS4zNDEsNTMuMzA4IDIuMzEsNTUuMDc4IEwxMi41MzEsNzMuNzIzIEw5NS44NDgsMjUuNjExIEw5Mi42NTMsMTkuNzgyIEM5Mi4wMzgsMTguNjYgOTAuODcsMTcuOTkgODkuNTI5LDE3Ljk5IEM4OC44MjUsMTcuOTkgODguMTE5LDE4LjE4MiA4Ny40ODksMTguNTQ3IEw4MC4xNzIsMjIuNzcyIEM4MC4xNjEsMjIuNzc4IDgwLjE0OSwyMi43ODIgODAuMTM3LDIyLjc4NSBDNzguMzQ2LDIzLjgxMSA3Ni4zNDEsMjQuMzU0IDc0LjMzNiwyNC4zNTQgQzcxLjU4OCwyNC4zNTQgNjkuMDMzLDIzLjM0NyA2Ny4xNDIsMjEuNTE5IEw2Ni44NjQsMjEuMjQ5IEM2Ni4yNzcsMjAuNjM0IDY1Ljc3NCwxOS45NDcgNjUuMzY3LDE5LjIwMyBDNjUuMzYsMTkuMTkyIDY1LjM1NiwxOS4xNzkgNjUuMzU0LDE5LjE2NiBMNjUuMTYzLDE4LjgxOSBDNjUuMTU0LDE4LjgxMSA2NS4xNDYsMTguODAxIDY1LjE0LDE4Ljc5IEM2NC41MjUsMTcuNjY3IDYzLjM1NywxNi45OTcgNjIuMDE2LDE2Ljk5NyBMNjIuMDE2LDE2Ljk5NyBaIiBpZD0iRmlsbC03IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTQyLjQzNCw0OC44MDggTDQyLjQzNCw0OC44MDggQzM5LjkyNCw0OC44MDcgMzcuNzM3LDQ3LjU1IDM2LjU4Miw0NS40NDMgQzM0Ljc3MSw0Mi4xMzkgMzYuMTQ0LDM3LjgwOSAzOS42NDEsMzUuNzg5IEw1MS45MzIsMjguNjkxIEM1My4xMDMsMjguMDE1IDU0LjQxMywyNy42NTggNTUuNzIxLDI3LjY1OCBDNTguMjMxLDI3LjY1OCA2MC40MTgsMjguOTE2IDYxLjU3MywzMS4wMjMgQzYzLjM4NCwzNC4zMjcgNjIuMDEyLDM4LjY1NyA1OC41MTQsNDAuNjc3IEw0Ni4yMjMsNDcuNzc1IEM0NS4wNTMsNDguNDUgNDMuNzQyLDQ4LjgwOCA0Mi40MzQsNDguODA4IEw0Mi40MzQsNDguODA4IFogTTU1LjcyMSwyOC4xMjUgQzU0LjQ5NSwyOC4xMjUgNTMuMjY1LDI4LjQ2MSA1Mi4xNjYsMjkuMDk2IEwzOS44NzUsMzYuMTk0IEMzNi41OTYsMzguMDg3IDM1LjMwMiw0Mi4xMzYgMzYuOTkyLDQ1LjIxOCBDMzguMDYzLDQ3LjE3MyA0MC4wOTgsNDguMzQgNDIuNDM0LDQ4LjM0IEM0My42NjEsNDguMzQgNDQuODksNDguMDA1IDQ1Ljk5LDQ3LjM3IEw1OC4yODEsNDAuMjcyIEM2MS41NiwzOC4zNzkgNjIuODUzLDM0LjMzIDYxLjE2NCwzMS4yNDggQzYwLjA5MiwyOS4yOTMgNTguMDU4LDI4LjEyNSA1NS43MjEsMjguMTI1IEw1NS43MjEsMjguMTI1IFoiIGlkPSJGaWxsLTgiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjU4OCwyLjQwNyBDMTQ5LjU4OCwyLjQwNyAxNTUuNzY4LDUuOTc1IDE1Ni4zMjUsNi4yOTcgTDE1Ni4zMjUsNy4xODQgQzE1Ni4zMjUsNy4zNiAxNTYuMzM4LDcuNTQ0IDE1Ni4zNjIsNy43MzMgQzE1Ni4zNzMsNy44MTQgMTU2LjM4Miw3Ljg5NCAxNTYuMzksNy45NzUgQzE1Ni41Myw5LjM5IDE1Ny4zNjMsMTAuOTczIDE1OC40OTUsMTEuOTc0IEwxNjUuODkxLDE4LjUxOSBDMTY2LjA2OCwxOC42NzUgMTY2LjI0OSwxOC44MTQgMTY2LjQzMiwxOC45MzQgQzE2OC4wMTEsMTkuOTc0IDE2OS4zODIsMTkuNCAxNjkuNDk0LDE3LjY1MiBDMTY5LjU0MywxNi44NjggMTY5LjU1MSwxNi4wNTcgMTY5LjUxNywxNS4yMjMgTDE2OS41MTQsMTUuMDYzIEwxNjkuNTE0LDEzLjkxMiBDMTcwLjc4LDE0LjY0MiAxOTUuNTAxLDI4LjkxNSAxOTUuNTAxLDI4LjkxNSBMMTk1LjUwMSw4Mi45MTUgQzE5NS41MDEsODQuMDA1IDE5NC43MzEsODQuNDQ1IDE5My43ODEsODMuODk3IEwxNTEuMzA4LDU5LjM3NCBDMTUwLjM1OCw1OC44MjYgMTQ5LjU4OCw1Ny40OTcgMTQ5LjU4OCw1Ni40MDggTDE0OS41ODgsMjIuMzc1IiBpZD0iRmlsbC05IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE5NC41NTMsODQuMjUgQzE5NC4yOTYsODQuMjUgMTk0LjAxMyw4NC4xNjUgMTkzLjcyMiw4My45OTcgTDE1MS4yNSw1OS40NzYgQzE1MC4yNjksNTguOTA5IDE0OS40NzEsNTcuNTMzIDE0OS40NzEsNTYuNDA4IEwxNDkuNDcxLDIyLjM3NSBMMTQ5LjcwNSwyMi4zNzUgTDE0OS43MDUsNTYuNDA4IEMxNDkuNzA1LDU3LjQ1OSAxNTAuNDUsNTguNzQ0IDE1MS4zNjYsNTkuMjc0IEwxOTMuODM5LDgzLjc5NSBDMTk0LjI2Myw4NC4wNCAxOTQuNjU1LDg0LjA4MyAxOTQuOTQyLDgzLjkxNyBDMTk1LjIyNyw4My43NTMgMTk1LjM4NCw4My4zOTcgMTk1LjM4NCw4Mi45MTUgTDE5NS4zODQsMjguOTgyIEMxOTQuMTAyLDI4LjI0MiAxNzIuMTA0LDE1LjU0MiAxNjkuNjMxLDE0LjExNCBMMTY5LjYzNCwxNS4yMiBDMTY5LjY2OCwxNi4wNTIgMTY5LjY2LDE2Ljg3NCAxNjkuNjEsMTcuNjU5IEMxNjkuNTU2LDE4LjUwMyAxNjkuMjE0LDE5LjEyMyAxNjguNjQ3LDE5LjQwNSBDMTY4LjAyOCwxOS43MTQgMTY3LjE5NywxOS41NzggMTY2LjM2NywxOS4wMzIgQzE2Ni4xODEsMTguOTA5IDE2NS45OTUsMTguNzY2IDE2NS44MTQsMTguNjA2IEwxNTguNDE3LDEyLjA2MiBDMTU3LjI1OSwxMS4wMzYgMTU2LjQxOCw5LjQzNyAxNTYuMjc0LDcuOTg2IEMxNTYuMjY2LDcuOTA3IDE1Ni4yNTcsNy44MjcgMTU2LjI0Nyw3Ljc0OCBDMTU2LjIyMSw3LjU1NSAxNTYuMjA5LDcuMzY1IDE1Ni4yMDksNy4xODQgTDE1Ni4yMDksNi4zNjQgQzE1NS4zNzUsNS44ODMgMTQ5LjUyOSwyLjUwOCAxNDkuNTI5LDIuNTA4IEwxNDkuNjQ2LDIuMzA2IEMxNDkuNjQ2LDIuMzA2IDE1NS44MjcsNS44NzQgMTU2LjM4NCw2LjE5NiBMMTU2LjQ0Miw2LjIzIEwxNTYuNDQyLDcuMTg0IEMxNTYuNDQyLDcuMzU1IDE1Ni40NTQsNy41MzUgMTU2LjQ3OCw3LjcxNyBDMTU2LjQ4OSw3LjggMTU2LjQ5OSw3Ljg4MiAxNTYuNTA3LDcuOTYzIEMxNTYuNjQ1LDkuMzU4IDE1Ny40NTUsMTAuODk4IDE1OC41NzIsMTEuODg2IEwxNjUuOTY5LDE4LjQzMSBDMTY2LjE0MiwxOC41ODQgMTY2LjMxOSwxOC43MiAxNjYuNDk2LDE4LjgzNyBDMTY3LjI1NCwxOS4zMzYgMTY4LDE5LjQ2NyAxNjguNTQzLDE5LjE5NiBDMTY5LjAzMywxOC45NTMgMTY5LjMyOSwxOC40MDEgMTY5LjM3NywxNy42NDUgQzE2OS40MjcsMTYuODY3IDE2OS40MzQsMTYuMDU0IDE2OS40MDEsMTUuMjI4IEwxNjkuMzk3LDE1LjA2NSBMMTY5LjM5NywxMy43MSBMMTY5LjU3MiwxMy44MSBDMTcwLjgzOSwxNC41NDEgMTk1LjU1OSwyOC44MTQgMTk1LjU1OSwyOC44MTQgTDE5NS42MTgsMjguODQ3IEwxOTUuNjE4LDgyLjkxNSBDMTk1LjYxOCw4My40ODQgMTk1LjQyLDgzLjkxMSAxOTUuMDU5LDg0LjExOSBDMTk0LjkwOCw4NC4yMDYgMTk0LjczNyw4NC4yNSAxOTQuNTUzLDg0LjI1IiBpZD0iRmlsbC0xMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDUuNjg1LDU2LjE2MSBMMTY5LjgsNzAuMDgzIEwxNDMuODIyLDg1LjA4MSBMMTQyLjM2LDg0Ljc3NCBDMTM1LjgyNiw4Mi42MDQgMTI4LjczMiw4MS4wNDYgMTIxLjM0MSw4MC4xNTggQzExNi45NzYsNzkuNjM0IDExMi42NzgsODEuMjU0IDExMS43NDMsODMuNzc4IEMxMTEuNTA2LDg0LjQxNCAxMTEuNTAzLDg1LjA3MSAxMTEuNzMyLDg1LjcwNiBDMTEzLjI3LDg5Ljk3MyAxMTUuOTY4LDk0LjA2OSAxMTkuNzI3LDk3Ljg0MSBMMTIwLjI1OSw5OC42ODYgQzEyMC4yNiw5OC42ODUgOTQuMjgyLDExMy42ODMgOTQuMjgyLDExMy42ODMgTDcwLjE2Nyw5OS43NjEgTDE0NS42ODUsNTYuMTYxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik05NC4yODIsMTEzLjgxOCBMOTQuMjIzLDExMy43ODUgTDY5LjkzMyw5OS43NjEgTDcwLjEwOCw5OS42NiBMMTQ1LjY4NSw1Ni4wMjYgTDE0NS43NDMsNTYuMDU5IEwxNzAuMDMzLDcwLjA4MyBMMTQzLjg0Miw4NS4yMDUgTDE0My43OTcsODUuMTk1IEMxNDMuNzcyLDg1LjE5IDE0Mi4zMzYsODQuODg4IDE0Mi4zMzYsODQuODg4IEMxMzUuNzg3LDgyLjcxNCAxMjguNzIzLDgxLjE2MyAxMjEuMzI3LDgwLjI3NCBDMTIwLjc4OCw4MC4yMDkgMTIwLjIzNiw4MC4xNzcgMTE5LjY4OSw4MC4xNzcgQzExNS45MzEsODAuMTc3IDExMi42MzUsODEuNzA4IDExMS44NTIsODMuODE5IEMxMTEuNjI0LDg0LjQzMiAxMTEuNjIxLDg1LjA1MyAxMTEuODQyLDg1LjY2NyBDMTEzLjM3Nyw4OS45MjUgMTE2LjA1OCw5My45OTMgMTE5LjgxLDk3Ljc1OCBMMTE5LjgyNiw5Ny43NzkgTDEyMC4zNTIsOTguNjE0IEMxMjAuMzU0LDk4LjYxNyAxMjAuMzU2LDk4LjYyIDEyMC4zNTgsOTguNjI0IEwxMjAuNDIyLDk4LjcyNiBMMTIwLjMxNyw5OC43ODcgQzEyMC4yNjQsOTguODE4IDk0LjU5OSwxMTMuNjM1IDk0LjM0LDExMy43ODUgTDk0LjI4MiwxMTMuODE4IEw5NC4yODIsMTEzLjgxOCBaIE03MC40MDEsOTkuNzYxIEw5NC4yODIsMTEzLjU0OSBMMTE5LjA4NCw5OS4yMjkgQzExOS42Myw5OC45MTQgMTE5LjkzLDk4Ljc0IDEyMC4xMDEsOTguNjU0IEwxMTkuNjM1LDk3LjkxNCBDMTE1Ljg2NCw5NC4xMjcgMTEzLjE2OCw5MC4wMzMgMTExLjYyMiw4NS43NDYgQzExMS4zODIsODUuMDc5IDExMS4zODYsODQuNDA0IDExMS42MzMsODMuNzM4IEMxMTIuNDQ4LDgxLjUzOSAxMTUuODM2LDc5Ljk0MyAxMTkuNjg5LDc5Ljk0MyBDMTIwLjI0Niw3OS45NDMgMTIwLjgwNiw3OS45NzYgMTIxLjM1NSw4MC4wNDIgQzEyOC43NjcsODAuOTMzIDEzNS44NDYsODIuNDg3IDE0Mi4zOTYsODQuNjYzIEMxNDMuMjMyLDg0LjgzOCAxNDMuNjExLDg0LjkxNyAxNDMuNzg2LDg0Ljk2NyBMMTY5LjU2Niw3MC4wODMgTDE0NS42ODUsNTYuMjk1IEw3MC40MDEsOTkuNzYxIEw3MC40MDEsOTkuNzYxIFoiIGlkPSJGaWxsLTEyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2Ny4yMywxOC45NzkgTDE2Ny4yMyw2OS44NSBMMTM5LjkwOSw4NS42MjMgTDEzMy40NDgsNzEuNDU2IEMxMzIuNTM4LDY5LjQ2IDEzMC4wMiw2OS43MTggMTI3LjgyNCw3Mi4wMyBDMTI2Ljc2OSw3My4xNCAxMjUuOTMxLDc0LjU4NSAxMjUuNDk0LDc2LjA0OCBMMTE5LjAzNCw5Ny42NzYgTDkxLjcxMiwxMTMuNDUgTDkxLjcxMiw2Mi41NzkgTDE2Ny4yMywxOC45NzkiIGlkPSJGaWxsLTEzIiBmaWxsPSIjRkZGRkZGIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTkxLjcxMiwxMTMuNTY3IEM5MS42OTIsMTEzLjU2NyA5MS42NzIsMTEzLjU2MSA5MS42NTMsMTEzLjU1MSBDOTEuNjE4LDExMy41MyA5MS41OTUsMTEzLjQ5MiA5MS41OTUsMTEzLjQ1IEw5MS41OTUsNjIuNTc5IEM5MS41OTUsNjIuNTM3IDkxLjYxOCw2Mi40OTkgOTEuNjUzLDYyLjQ3OCBMMTY3LjE3MiwxOC44NzggQzE2Ny4yMDgsMTguODU3IDE2Ny4yNTIsMTguODU3IDE2Ny4yODgsMTguODc4IEMxNjcuMzI0LDE4Ljg5OSAxNjcuMzQ3LDE4LjkzNyAxNjcuMzQ3LDE4Ljk3OSBMMTY3LjM0Nyw2OS44NSBDMTY3LjM0Nyw2OS44OTEgMTY3LjMyNCw2OS45MyAxNjcuMjg4LDY5Ljk1IEwxMzkuOTY3LDg1LjcyNSBDMTM5LjkzOSw4NS43NDEgMTM5LjkwNSw4NS43NDUgMTM5Ljg3Myw4NS43MzUgQzEzOS44NDIsODUuNzI1IDEzOS44MTYsODUuNzAyIDEzOS44MDIsODUuNjcyIEwxMzMuMzQyLDcxLjUwNCBDMTMyLjk2Nyw3MC42ODIgMTMyLjI4LDcwLjIyOSAxMzEuNDA4LDcwLjIyOSBDMTMwLjMxOSw3MC4yMjkgMTI5LjA0NCw3MC45MTUgMTI3LjkwOCw3Mi4xMSBDMTI2Ljg3NCw3My4yIDEyNi4wMzQsNzQuNjQ3IDEyNS42MDYsNzYuMDgyIEwxMTkuMTQ2LDk3LjcwOSBDMTE5LjEzNyw5Ny43MzggMTE5LjExOCw5Ny43NjIgMTE5LjA5Miw5Ny43NzcgTDkxLjc3LDExMy41NTEgQzkxLjc1MiwxMTMuNTYxIDkxLjczMiwxMTMuNTY3IDkxLjcxMiwxMTMuNTY3IEw5MS43MTIsMTEzLjU2NyBaIE05MS44MjksNjIuNjQ3IEw5MS44MjksMTEzLjI0OCBMMTE4LjkzNSw5Ny41OTggTDEyNS4zODIsNzYuMDE1IEMxMjUuODI3LDc0LjUyNSAxMjYuNjY0LDczLjA4MSAxMjcuNzM5LDcxLjk1IEMxMjguOTE5LDcwLjcwOCAxMzAuMjU2LDY5Ljk5NiAxMzEuNDA4LDY5Ljk5NiBDMTMyLjM3Nyw2OS45OTYgMTMzLjEzOSw3MC40OTcgMTMzLjU1NCw3MS40MDcgTDEzOS45NjEsODUuNDU4IEwxNjcuMTEzLDY5Ljc4MiBMMTY3LjExMywxOS4xODEgTDkxLjgyOSw2Mi42NDcgTDkxLjgyOSw2Mi42NDcgWiIgaWQ9IkZpbGwtMTQiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTY4LjU0MywxOS4yMTMgTDE2OC41NDMsNzAuMDgzIEwxNDEuMjIxLDg1Ljg1NyBMMTM0Ljc2MSw3MS42ODkgQzEzMy44NTEsNjkuNjk0IDEzMS4zMzMsNjkuOTUxIDEyOS4xMzcsNzIuMjYzIEMxMjguMDgyLDczLjM3NCAxMjcuMjQ0LDc0LjgxOSAxMjYuODA3LDc2LjI4MiBMMTIwLjM0Niw5Ny45MDkgTDkzLjAyNSwxMTMuNjgzIEw5My4wMjUsNjIuODEzIEwxNjguNTQzLDE5LjIxMyIgaWQ9IkZpbGwtMTUiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTMuMDI1LDExMy44IEM5My4wMDUsMTEzLjggOTIuOTg0LDExMy43OTUgOTIuOTY2LDExMy43ODUgQzkyLjkzMSwxMTMuNzY0IDkyLjkwOCwxMTMuNzI1IDkyLjkwOCwxMTMuNjg0IEw5Mi45MDgsNjIuODEzIEM5Mi45MDgsNjIuNzcxIDkyLjkzMSw2Mi43MzMgOTIuOTY2LDYyLjcxMiBMMTY4LjQ4NCwxOS4xMTIgQzE2OC41MiwxOS4wOSAxNjguNTY1LDE5LjA5IDE2OC42MDEsMTkuMTEyIEMxNjguNjM3LDE5LjEzMiAxNjguNjYsMTkuMTcxIDE2OC42NiwxOS4yMTIgTDE2OC42Niw3MC4wODMgQzE2OC42Niw3MC4xMjUgMTY4LjYzNyw3MC4xNjQgMTY4LjYwMSw3MC4xODQgTDE0MS4yOCw4NS45NTggQzE0MS4yNTEsODUuOTc1IDE0MS4yMTcsODUuOTc5IDE0MS4xODYsODUuOTY4IEMxNDEuMTU0LDg1Ljk1OCAxNDEuMTI5LDg1LjkzNiAxNDEuMTE1LDg1LjkwNiBMMTM0LjY1NSw3MS43MzggQzEzNC4yOCw3MC45MTUgMTMzLjU5Myw3MC40NjMgMTMyLjcyLDcwLjQ2MyBDMTMxLjYzMiw3MC40NjMgMTMwLjM1Nyw3MS4xNDggMTI5LjIyMSw3Mi4zNDQgQzEyOC4xODYsNzMuNDMzIDEyNy4zNDcsNzQuODgxIDEyNi45MTksNzYuMzE1IEwxMjAuNDU4LDk3Ljk0MyBDMTIwLjQ1LDk3Ljk3MiAxMjAuNDMxLDk3Ljk5NiAxMjAuNDA1LDk4LjAxIEw5My4wODMsMTEzLjc4NSBDOTMuMDY1LDExMy43OTUgOTMuMDQ1LDExMy44IDkzLjAyNSwxMTMuOCBMOTMuMDI1LDExMy44IFogTTkzLjE0Miw2Mi44ODEgTDkzLjE0MiwxMTMuNDgxIEwxMjAuMjQ4LDk3LjgzMiBMMTI2LjY5NSw3Ni4yNDggQzEyNy4xNCw3NC43NTggMTI3Ljk3Nyw3My4zMTUgMTI5LjA1Miw3Mi4xODMgQzEzMC4yMzEsNzAuOTQyIDEzMS41NjgsNzAuMjI5IDEzMi43Miw3MC4yMjkgQzEzMy42ODksNzAuMjI5IDEzNC40NTIsNzAuNzMxIDEzNC44NjcsNzEuNjQxIEwxNDEuMjc0LDg1LjY5MiBMMTY4LjQyNiw3MC4wMTYgTDE2OC40MjYsMTkuNDE1IEw5My4xNDIsNjIuODgxIEw5My4xNDIsNjIuODgxIFoiIGlkPSJGaWxsLTE2IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS44LDcwLjA4MyBMMTQyLjQ3OCw4NS44NTcgTDEzNi4wMTgsNzEuNjg5IEMxMzUuMTA4LDY5LjY5NCAxMzIuNTksNjkuOTUxIDEzMC4zOTMsNzIuMjYzIEMxMjkuMzM5LDczLjM3NCAxMjguNSw3NC44MTkgMTI4LjA2NCw3Ni4yODIgTDEyMS42MDMsOTcuOTA5IEw5NC4yODIsMTEzLjY4MyBMOTQuMjgyLDYyLjgxMyBMMTY5LjgsMTkuMjEzIEwxNjkuOCw3MC4wODMgWiIgaWQ9IkZpbGwtMTciIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTQuMjgyLDExMy45MTcgQzk0LjI0MSwxMTMuOTE3IDk0LjIwMSwxMTMuOTA3IDk0LjE2NSwxMTMuODg2IEM5NC4wOTMsMTEzLjg0NSA5NC4wNDgsMTEzLjc2NyA5NC4wNDgsMTEzLjY4NCBMOTQuMDQ4LDYyLjgxMyBDOTQuMDQ4LDYyLjczIDk0LjA5Myw2Mi42NTIgOTQuMTY1LDYyLjYxMSBMMTY5LjY4MywxOS4wMSBDMTY5Ljc1NSwxOC45NjkgMTY5Ljg0NCwxOC45NjkgMTY5LjkxNywxOS4wMSBDMTY5Ljk4OSwxOS4wNTIgMTcwLjAzMywxOS4xMjkgMTcwLjAzMywxOS4yMTIgTDE3MC4wMzMsNzAuMDgzIEMxNzAuMDMzLDcwLjE2NiAxNjkuOTg5LDcwLjI0NCAxNjkuOTE3LDcwLjI4NSBMMTQyLjU5NSw4Ni4wNiBDMTQyLjUzOCw4Ni4wOTIgMTQyLjQ2OSw4Ni4xIDE0Mi40MDcsODYuMDggQzE0Mi4zNDQsODYuMDYgMTQyLjI5Myw4Ni4wMTQgMTQyLjI2Niw4NS45NTQgTDEzNS44MDUsNzEuNzg2IEMxMzUuNDQ1LDcwLjk5NyAxMzQuODEzLDcwLjU4IDEzMy45NzcsNzAuNTggQzEzMi45MjEsNzAuNTggMTMxLjY3Niw3MS4yNTIgMTMwLjU2Miw3Mi40MjQgQzEyOS41NCw3My41MDEgMTI4LjcxMSw3NC45MzEgMTI4LjI4Nyw3Ni4zNDggTDEyMS44MjcsOTcuOTc2IEMxMjEuODEsOTguMDM0IDEyMS43NzEsOTguMDgyIDEyMS43Miw5OC4xMTIgTDk0LjM5OCwxMTMuODg2IEM5NC4zNjIsMTEzLjkwNyA5NC4zMjIsMTEzLjkxNyA5NC4yODIsMTEzLjkxNyBMOTQuMjgyLDExMy45MTcgWiBNOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDExMy4yNzkgTDEyMS40MDYsOTcuNzU0IEwxMjcuODQsNzYuMjE1IEMxMjguMjksNzQuNzA4IDEyOS4xMzcsNzMuMjQ3IDEzMC4yMjQsNzIuMTAzIEMxMzEuNDI1LDcwLjgzOCAxMzIuNzkzLDcwLjExMiAxMzMuOTc3LDcwLjExMiBDMTM0Ljk5NSw3MC4xMTIgMTM1Ljc5NSw3MC42MzggMTM2LjIzLDcxLjU5MiBMMTQyLjU4NCw4NS41MjYgTDE2OS41NjYsNjkuOTQ4IEwxNjkuNTY2LDE5LjYxNyBMOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDYyLjk0OCBaIiBpZD0iRmlsbC0xOCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMDkuODk0LDkyLjk0MyBMMTA5Ljg5NCw5Mi45NDMgQzEwOC4xMiw5Mi45NDMgMTA2LjY1Myw5Mi4yMTggMTA1LjY1LDkwLjgyMyBDMTA1LjU4Myw5MC43MzEgMTA1LjU5Myw5MC42MSAxMDUuNjczLDkwLjUyOSBDMTA1Ljc1Myw5MC40NDggMTA1Ljg4LDkwLjQ0IDEwNS45NzQsOTAuNTA2IEMxMDYuNzU0LDkxLjA1MyAxMDcuNjc5LDkxLjMzMyAxMDguNzI0LDkxLjMzMyBDMTEwLjA0Nyw5MS4zMzMgMTExLjQ3OCw5MC44OTQgMTEyLjk4LDkwLjAyNyBDMTE4LjI5MSw4Ni45NiAxMjIuNjExLDc5LjUwOSAxMjIuNjExLDczLjQxNiBDMTIyLjYxMSw3MS40ODkgMTIyLjE2OSw2OS44NTYgMTIxLjMzMyw2OC42OTIgQzEyMS4yNjYsNjguNiAxMjEuMjc2LDY4LjQ3MyAxMjEuMzU2LDY4LjM5MiBDMTIxLjQzNiw2OC4zMTEgMTIxLjU2Myw2OC4yOTkgMTIxLjY1Niw2OC4zNjUgQzEyMy4zMjcsNjkuNTM3IDEyNC4yNDcsNzEuNzQ2IDEyNC4yNDcsNzQuNTg0IEMxMjQuMjQ3LDgwLjgyNiAxMTkuODIxLDg4LjQ0NyAxMTQuMzgyLDkxLjU4NyBDMTEyLjgwOCw5Mi40OTUgMTExLjI5OCw5Mi45NDMgMTA5Ljg5NCw5Mi45NDMgTDEwOS44OTQsOTIuOTQzIFogTTEwNi45MjUsOTEuNDAxIEMxMDcuNzM4LDkyLjA1MiAxMDguNzQ1LDkyLjI3OCAxMDkuODkzLDkyLjI3OCBMMTA5Ljg5NCw5Mi4yNzggQzExMS4yMTUsOTIuMjc4IDExMi42NDcsOTEuOTUxIDExNC4xNDgsOTEuMDg0IEMxMTkuNDU5LDg4LjAxNyAxMjMuNzgsODAuNjIxIDEyMy43OCw3NC41MjggQzEyMy43OCw3Mi41NDkgMTIzLjMxNyw3MC45MjkgMTIyLjQ1NCw2OS43NjcgQzEyMi44NjUsNzAuODAyIDEyMy4wNzksNzIuMDQyIDEyMy4wNzksNzMuNDAyIEMxMjMuMDc5LDc5LjY0NSAxMTguNjUzLDg3LjI4NSAxMTMuMjE0LDkwLjQyNSBDMTExLjY0LDkxLjMzNCAxMTAuMTMsOTEuNzQyIDEwOC43MjQsOTEuNzQyIEMxMDguMDgzLDkxLjc0MiAxMDcuNDgxLDkxLjU5MyAxMDYuOTI1LDkxLjQwMSBMMTA2LjkyNSw5MS40MDEgWiIgaWQ9IkZpbGwtMTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjA5Nyw5MC4yMyBDMTE4LjQ4MSw4Ny4xMjIgMTIyLjg0NSw3OS41OTQgMTIyLjg0NSw3My40MTYgQzEyMi44NDUsNzEuMzY1IDEyMi4zNjIsNjkuNzI0IDEyMS41MjIsNjguNTU2IEMxMTkuNzM4LDY3LjMwNCAxMTcuMTQ4LDY3LjM2MiAxMTQuMjY1LDY5LjAyNiBDMTA4Ljg4MSw3Mi4xMzQgMTA0LjUxNyw3OS42NjIgMTA0LjUxNyw4NS44NCBDMTA0LjUxNyw4Ny44OTEgMTA1LDg5LjUzMiAxMDUuODQsOTAuNyBDMTA3LjYyNCw5MS45NTIgMTEwLjIxNCw5MS44OTQgMTEzLjA5Nyw5MC4yMyIgaWQ9IkZpbGwtMjAiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTA4LjcyNCw5MS42MTQgTDEwOC43MjQsOTEuNjE0IEMxMDcuNTgyLDkxLjYxNCAxMDYuNTY2LDkxLjQwMSAxMDUuNzA1LDkwLjc5NyBDMTA1LjY4NCw5MC43ODMgMTA1LjY2NSw5MC44MTEgMTA1LjY1LDkwLjc5IEMxMDQuNzU2LDg5LjU0NiAxMDQuMjgzLDg3Ljg0MiAxMDQuMjgzLDg1LjgxNyBDMTA0LjI4Myw3OS41NzUgMTA4LjcwOSw3MS45NTMgMTE0LjE0OCw2OC44MTIgQzExNS43MjIsNjcuOTA0IDExNy4yMzIsNjcuNDQ5IDExOC42MzgsNjcuNDQ5IEMxMTkuNzgsNjcuNDQ5IDEyMC43OTYsNjcuNzU4IDEyMS42NTYsNjguMzYyIEMxMjEuNjc4LDY4LjM3NyAxMjEuNjk3LDY4LjM5NyAxMjEuNzEyLDY4LjQxOCBDMTIyLjYwNiw2OS42NjIgMTIzLjA3OSw3MS4zOSAxMjMuMDc5LDczLjQxNSBDMTIzLjA3OSw3OS42NTggMTE4LjY1Myw4Ny4xOTggMTEzLjIxNCw5MC4zMzggQzExMS42NCw5MS4yNDcgMTEwLjEzLDkxLjYxNCAxMDguNzI0LDkxLjYxNCBMMTA4LjcyNCw5MS42MTQgWiBNMTA2LjAwNiw5MC41MDUgQzEwNi43OCw5MS4wMzcgMTA3LjY5NCw5MS4yODEgMTA4LjcyNCw5MS4yODEgQzExMC4wNDcsOTEuMjgxIDExMS40NzgsOTAuODY4IDExMi45OCw5MC4wMDEgQzExOC4yOTEsODYuOTM1IDEyMi42MTEsNzkuNDk2IDEyMi42MTEsNzMuNDAzIEMxMjIuNjExLDcxLjQ5NCAxMjIuMTc3LDY5Ljg4IDEyMS4zNTYsNjguNzE4IEMxMjAuNTgyLDY4LjE4NSAxMTkuNjY4LDY3LjkxOSAxMTguNjM4LDY3LjkxOSBDMTE3LjMxNSw2Ny45MTkgMTE1Ljg4Myw2OC4zNiAxMTQuMzgyLDY5LjIyNyBDMTA5LjA3MSw3Mi4yOTMgMTA0Ljc1MSw3OS43MzMgMTA0Ljc1MSw4NS44MjYgQzEwNC43NTEsODcuNzM1IDEwNS4xODUsODkuMzQzIDEwNi4wMDYsOTAuNTA1IEwxMDYuMDA2LDkwLjUwNSBaIiBpZD0iRmlsbC0yMSIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDkuMzE4LDcuMjYyIEwxMzkuMzM0LDE2LjE0IEwxNTUuMjI3LDI3LjE3MSBMMTYwLjgxNiwyMS4wNTkgTDE0OS4zMTgsNy4yNjIiIGlkPSJGaWxsLTIyIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS42NzYsMTMuODQgTDE1OS45MjgsMTkuNDY3IEMxNTYuMjg2LDIxLjU3IDE1MC40LDIxLjU4IDE0Ni43ODEsMTkuNDkxIEMxNDMuMTYxLDE3LjQwMiAxNDMuMTgsMTQuMDAzIDE0Ni44MjIsMTEuOSBMMTU2LjMxNyw2LjI5MiBMMTQ5LjU4OCwyLjQwNyBMNjcuNzUyLDQ5LjQ3OCBMMTEzLjY3NSw3NS45OTIgTDExNi43NTYsNzQuMjEzIEMxMTcuMzg3LDczLjg0OCAxMTcuNjI1LDczLjMxNSAxMTcuMzc0LDcyLjgyMyBDMTE1LjAxNyw2OC4xOTEgMTE0Ljc4MSw2My4yNzcgMTE2LjY5MSw1OC41NjEgQzEyMi4zMjksNDQuNjQxIDE0MS4yLDMzLjc0NiAxNjUuMzA5LDMwLjQ5MSBDMTczLjQ3OCwyOS4zODggMTgxLjk4OSwyOS41MjQgMTkwLjAxMywzMC44ODUgQzE5MC44NjUsMzEuMDMgMTkxLjc4OSwzMC44OTMgMTkyLjQyLDMwLjUyOCBMMTk1LjUwMSwyOC43NSBMMTY5LjY3NiwxMy44NCIgaWQ9IkZpbGwtMjMiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3Ni40NTkgQzExMy41OTQsNzYuNDU5IDExMy41MTQsNzYuNDM4IDExMy40NDIsNzYuMzk3IEw2Ny41MTgsNDkuODgyIEM2Ny4zNzQsNDkuNzk5IDY3LjI4NCw0OS42NDUgNjcuMjg1LDQ5LjQ3OCBDNjcuMjg1LDQ5LjMxMSA2Ny4zNzQsNDkuMTU3IDY3LjUxOSw0OS4wNzMgTDE0OS4zNTUsMi4wMDIgQzE0OS40OTksMS45MTkgMTQ5LjY3NywxLjkxOSAxNDkuODIxLDIuMDAyIEwxNTYuNTUsNS44ODcgQzE1Ni43NzQsNi4wMTcgMTU2Ljg1LDYuMzAyIDE1Ni43MjIsNi41MjYgQzE1Ni41OTIsNi43NDkgMTU2LjMwNyw2LjgyNiAxNTYuMDgzLDYuNjk2IEwxNDkuNTg3LDIuOTQ2IEw2OC42ODcsNDkuNDc5IEwxMTMuNjc1LDc1LjQ1MiBMMTE2LjUyMyw3My44MDggQzExNi43MTUsNzMuNjk3IDExNy4xNDMsNzMuMzk5IDExNi45NTgsNzMuMDM1IEMxMTQuNTQyLDY4LjI4NyAxMTQuMyw2My4yMjEgMTE2LjI1OCw1OC4zODUgQzExOS4wNjQsNTEuNDU4IDEyNS4xNDMsNDUuMTQzIDEzMy44NCw0MC4xMjIgQzE0Mi40OTcsMzUuMTI0IDE1My4zNTgsMzEuNjMzIDE2NS4yNDcsMzAuMDI4IEMxNzMuNDQ1LDI4LjkyMSAxODIuMDM3LDI5LjA1OCAxOTAuMDkxLDMwLjQyNSBDMTkwLjgzLDMwLjU1IDE5MS42NTIsMzAuNDMyIDE5Mi4xODYsMzAuMTI0IEwxOTQuNTY3LDI4Ljc1IEwxNjkuNDQyLDE0LjI0NCBDMTY5LjIxOSwxNC4xMTUgMTY5LjE0MiwxMy44MjkgMTY5LjI3MSwxMy42MDYgQzE2OS40LDEzLjM4MiAxNjkuNjg1LDEzLjMwNiAxNjkuOTA5LDEzLjQzNSBMMTk1LjczNCwyOC4zNDUgQzE5NS44NzksMjguNDI4IDE5NS45NjgsMjguNTgzIDE5NS45NjgsMjguNzUgQzE5NS45NjgsMjguOTE2IDE5NS44NzksMjkuMDcxIDE5NS43MzQsMjkuMTU0IEwxOTIuNjUzLDMwLjkzMyBDMTkxLjkzMiwzMS4zNSAxOTAuODksMzEuNTA4IDE4OS45MzUsMzEuMzQ2IEMxODEuOTcyLDI5Ljk5NSAxNzMuNDc4LDI5Ljg2IDE2NS4zNzIsMzAuOTU0IEMxNTMuNjAyLDMyLjU0MyAxNDIuODYsMzUuOTkzIDEzNC4zMDcsNDAuOTMxIEMxMjUuNzkzLDQ1Ljg0NyAxMTkuODUxLDUyLjAwNCAxMTcuMTI0LDU4LjczNiBDMTE1LjI3LDYzLjMxNCAxMTUuNTAxLDY4LjExMiAxMTcuNzksNzIuNjExIEMxMTguMTYsNzMuMzM2IDExNy44NDUsNzQuMTI0IDExNi45OSw3NC42MTcgTDExMy45MDksNzYuMzk3IEMxMTMuODM2LDc2LjQzOCAxMTMuNzU2LDc2LjQ1OSAxMTMuNjc1LDc2LjQ1OSIgaWQ9IkZpbGwtMjQiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUzLjMxNiwyMS4yNzkgQzE1MC45MDMsMjEuMjc5IDE0OC40OTUsMjAuNzUxIDE0Ni42NjQsMTkuNjkzIEMxNDQuODQ2LDE4LjY0NCAxNDMuODQ0LDE3LjIzMiAxNDMuODQ0LDE1LjcxOCBDMTQzLjg0NCwxNC4xOTEgMTQ0Ljg2LDEyLjc2MyAxNDYuNzA1LDExLjY5OCBMMTU2LjE5OCw2LjA5MSBDMTU2LjMwOSw2LjAyNSAxNTYuNDUyLDYuMDYyIDE1Ni41MTgsNi4xNzMgQzE1Ni41ODMsNi4yODQgMTU2LjU0Nyw2LjQyNyAxNTYuNDM2LDYuNDkzIEwxNDYuOTQsMTIuMTAyIEMxNDUuMjQ0LDEzLjA4MSAxNDQuMzEyLDE0LjM2NSAxNDQuMzEyLDE1LjcxOCBDMTQ0LjMxMiwxNy4wNTggMTQ1LjIzLDE4LjMyNiAxNDYuODk3LDE5LjI4OSBDMTUwLjQ0NiwyMS4zMzggMTU2LjI0LDIxLjMyNyAxNTkuODExLDE5LjI2NSBMMTY5LjU1OSwxMy42MzcgQzE2OS42NywxMy41NzMgMTY5LjgxMywxMy42MTEgMTY5Ljg3OCwxMy43MjMgQzE2OS45NDMsMTMuODM0IDE2OS45MDQsMTMuOTc3IDE2OS43OTMsMTQuMDQyIEwxNjAuMDQ1LDE5LjY3IEMxNTguMTg3LDIwLjc0MiAxNTUuNzQ5LDIxLjI3OSAxNTMuMzE2LDIxLjI3OSIgaWQ9IkZpbGwtMjUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3NS45OTIgTDY3Ljc2Miw0OS40ODQiIGlkPSJGaWxsLTI2IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMy42NzUsNzYuMzQyIEMxMTMuNjE1LDc2LjM0MiAxMTMuNTU1LDc2LjMyNyAxMTMuNSw3Ni4yOTUgTDY3LjU4Nyw0OS43ODcgQzY3LjQxOSw0OS42OSA2Ny4zNjIsNDkuNDc2IDY3LjQ1OSw0OS4zMDkgQzY3LjU1Niw0OS4xNDEgNjcuNzcsNDkuMDgzIDY3LjkzNyw0OS4xOCBMMTEzLjg1LDc1LjY4OCBDMTE0LjAxOCw3NS43ODUgMTE0LjA3NSw3NiAxMTMuOTc4LDc2LjE2NyBDMTEzLjkxNCw3Ni4yNzkgMTEzLjc5Niw3Ni4zNDIgMTEzLjY3NSw3Ni4zNDIiIGlkPSJGaWxsLTI3IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY3Ljc2Miw0OS40ODQgTDY3Ljc2MiwxMDMuNDg1IEM2Ny43NjIsMTA0LjU3NSA2OC41MzIsMTA1LjkwMyA2OS40ODIsMTA2LjQ1MiBMMTExLjk1NSwxMzAuOTczIEMxMTIuOTA1LDEzMS41MjIgMTEzLjY3NSwxMzEuMDgzIDExMy42NzUsMTI5Ljk5MyBMMTEzLjY3NSw3NS45OTIiIGlkPSJGaWxsLTI4IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMi43MjcsMTMxLjU2MSBDMTEyLjQzLDEzMS41NjEgMTEyLjEwNywxMzEuNDY2IDExMS43OCwxMzEuMjc2IEw2OS4zMDcsMTA2Ljc1NSBDNjguMjQ0LDEwNi4xNDIgNjcuNDEyLDEwNC43MDUgNjcuNDEyLDEwMy40ODUgTDY3LjQxMiw0OS40ODQgQzY3LjQxMiw0OS4yOSA2Ny41NjksNDkuMTM0IDY3Ljc2Miw0OS4xMzQgQzY3Ljk1Niw0OS4xMzQgNjguMTEzLDQ5LjI5IDY4LjExMyw0OS40ODQgTDY4LjExMywxMDMuNDg1IEM2OC4xMTMsMTA0LjQ0NSA2OC44MiwxMDUuNjY1IDY5LjY1NywxMDYuMTQ4IEwxMTIuMTMsMTMwLjY3IEMxMTIuNDc0LDEzMC44NjggMTEyLjc5MSwxMzAuOTEzIDExMywxMzAuNzkyIEMxMTMuMjA2LDEzMC42NzMgMTEzLjMyNSwxMzAuMzgxIDExMy4zMjUsMTI5Ljk5MyBMMTEzLjMyNSw3NS45OTIgQzExMy4zMjUsNzUuNzk4IDExMy40ODIsNzUuNjQxIDExMy42NzUsNzUuNjQxIEMxMTMuODY5LDc1LjY0MSAxMTQuMDI1LDc1Ljc5OCAxMTQuMDI1LDc1Ljk5MiBMMTE0LjAyNSwxMjkuOTkzIEMxMTQuMDI1LDEzMC42NDggMTEzLjc4NiwxMzEuMTQ3IDExMy4zNSwxMzEuMzk5IEMxMTMuMTYyLDEzMS41MDcgMTEyLjk1MiwxMzEuNTYxIDExMi43MjcsMTMxLjU2MSIgaWQ9IkZpbGwtMjkiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEyLjg2LDQwLjUxMiBDMTEyLjg2LDQwLjUxMiAxMTIuODYsNDAuNTEyIDExMi44NTksNDAuNTEyIEMxMTAuNTQxLDQwLjUxMiAxMDguMzYsMzkuOTkgMTA2LjcxNywzOS4wNDEgQzEwNS4wMTIsMzguMDU3IDEwNC4wNzQsMzYuNzI2IDEwNC4wNzQsMzUuMjkyIEMxMDQuMDc0LDMzLjg0NyAxMDUuMDI2LDMyLjUwMSAxMDYuNzU0LDMxLjUwNCBMMTE4Ljc5NSwyNC41NTEgQzEyMC40NjMsMjMuNTg5IDEyMi42NjksMjMuMDU4IDEyNS4wMDcsMjMuMDU4IEMxMjcuMzI1LDIzLjA1OCAxMjkuNTA2LDIzLjU4MSAxMzEuMTUsMjQuNTMgQzEzMi44NTQsMjUuNTE0IDEzMy43OTMsMjYuODQ1IDEzMy43OTMsMjguMjc4IEMxMzMuNzkzLDI5LjcyNCAxMzIuODQxLDMxLjA2OSAxMzEuMTEzLDMyLjA2NyBMMTE5LjA3MSwzOS4wMTkgQzExNy40MDMsMzkuOTgyIDExNS4xOTcsNDAuNTEyIDExMi44Niw0MC41MTIgTDExMi44Niw0MC41MTIgWiBNMTI1LjAwNywyMy43NTkgQzEyMi43OSwyMy43NTkgMTIwLjcwOSwyNC4yNTYgMTE5LjE0NiwyNS4xNTggTDEwNy4xMDQsMzIuMTEgQzEwNS42MDIsMzIuOTc4IDEwNC43NzQsMzQuMTA4IDEwNC43NzQsMzUuMjkyIEMxMDQuNzc0LDM2LjQ2NSAxMDUuNTg5LDM3LjU4MSAxMDcuMDY3LDM4LjQzNCBDMTA4LjYwNSwzOS4zMjMgMTEwLjY2MywzOS44MTIgMTEyLjg1OSwzOS44MTIgTDExMi44NiwzOS44MTIgQzExNS4wNzYsMzkuODEyIDExNy4xNTgsMzkuMzE1IDExOC43MjEsMzguNDEzIEwxMzAuNzYyLDMxLjQ2IEMxMzIuMjY0LDMwLjU5MyAxMzMuMDkyLDI5LjQ2MyAxMzMuMDkyLDI4LjI3OCBDMTMzLjA5MiwyNy4xMDYgMTMyLjI3OCwyNS45OSAxMzAuOCwyNS4xMzYgQzEyOS4yNjEsMjQuMjQ4IDEyNy4yMDQsMjMuNzU5IDEyNS4wMDcsMjMuNzU5IEwxMjUuMDA3LDIzLjc1OSBaIiBpZD0iRmlsbC0zMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNjUuNjMsMTYuMjE5IEwxNTkuODk2LDE5LjUzIEMxNTYuNzI5LDIxLjM1OCAxNTEuNjEsMjEuMzY3IDE0OC40NjMsMTkuNTUgQzE0NS4zMTYsMTcuNzMzIDE0NS4zMzIsMTQuNzc4IDE0OC40OTksMTIuOTQ5IEwxNTQuMjMzLDkuNjM5IEwxNjUuNjMsMTYuMjE5IiBpZD0iRmlsbC0zMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNTQuMjMzLDEwLjQ0OCBMMTY0LjIyOCwxNi4yMTkgTDE1OS41NDYsMTguOTIzIEMxNTguMTEyLDE5Ljc1IDE1Ni4xOTQsMjAuMjA2IDE1NC4xNDcsMjAuMjA2IEMxNTIuMTE4LDIwLjIwNiAxNTAuMjI0LDE5Ljc1NyAxNDguODE0LDE4Ljk0MyBDMTQ3LjUyNCwxOC4xOTkgMTQ2LjgxNCwxNy4yNDkgMTQ2LjgxNCwxNi4yNjkgQzE0Ni44MTQsMTUuMjc4IDE0Ny41MzcsMTQuMzE0IDE0OC44NSwxMy41NTYgTDE1NC4yMzMsMTAuNDQ4IE0xNTQuMjMzLDkuNjM5IEwxNDguNDk5LDEyLjk0OSBDMTQ1LjMzMiwxNC43NzggMTQ1LjMxNiwxNy43MzMgMTQ4LjQ2MywxOS41NSBDMTUwLjAzMSwyMC40NTUgMTUyLjA4NiwyMC45MDcgMTU0LjE0NywyMC45MDcgQzE1Ni4yMjQsMjAuOTA3IDE1OC4zMDYsMjAuNDQ3IDE1OS44OTYsMTkuNTMgTDE2NS42MywxNi4yMTkgTDE1NC4yMzMsOS42MzkiIGlkPSJGaWxsLTMyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NS40NDUsNzIuNjY3IEwxNDUuNDQ1LDcyLjY2NyBDMTQzLjY3Miw3Mi42NjcgMTQyLjIwNCw3MS44MTcgMTQxLjIwMiw3MC40MjIgQzE0MS4xMzUsNzAuMzMgMTQxLjE0NSw3MC4xNDcgMTQxLjIyNSw3MC4wNjYgQzE0MS4zMDUsNjkuOTg1IDE0MS40MzIsNjkuOTQ2IDE0MS41MjUsNzAuMDExIEMxNDIuMzA2LDcwLjU1OSAxNDMuMjMxLDcwLjgyMyAxNDQuMjc2LDcwLjgyMiBDMTQ1LjU5OCw3MC44MjIgMTQ3LjAzLDcwLjM3NiAxNDguNTMyLDY5LjUwOSBDMTUzLjg0Miw2Ni40NDMgMTU4LjE2Myw1OC45ODcgMTU4LjE2Myw1Mi44OTQgQzE1OC4xNjMsNTAuOTY3IDE1Ny43MjEsNDkuMzMyIDE1Ni44ODQsNDguMTY4IEMxNTYuODE4LDQ4LjA3NiAxNTYuODI4LDQ3Ljk0OCAxNTYuOTA4LDQ3Ljg2NyBDMTU2Ljk4OCw0Ny43ODYgMTU3LjExNCw0Ny43NzQgMTU3LjIwOCw0Ny44NCBDMTU4Ljg3OCw0OS4wMTIgMTU5Ljc5OCw1MS4yMiAxNTkuNzk4LDU0LjA1OSBDMTU5Ljc5OCw2MC4zMDEgMTU1LjM3Myw2OC4wNDYgMTQ5LjkzMyw3MS4xODYgQzE0OC4zNiw3Mi4wOTQgMTQ2Ljg1LDcyLjY2NyAxNDUuNDQ1LDcyLjY2NyBMMTQ1LjQ0NSw3Mi42NjcgWiBNMTQyLjQ3Niw3MSBDMTQzLjI5LDcxLjY1MSAxNDQuMjk2LDcyLjAwMiAxNDUuNDQ1LDcyLjAwMiBDMTQ2Ljc2Nyw3Mi4wMDIgMTQ4LjE5OCw3MS41NSAxNDkuNyw3MC42ODIgQzE1NS4wMSw2Ny42MTcgMTU5LjMzMSw2MC4xNTkgMTU5LjMzMSw1NC4wNjUgQzE1OS4zMzEsNTIuMDg1IDE1OC44NjgsNTAuNDM1IDE1OC4wMDYsNDkuMjcyIEMxNTguNDE3LDUwLjMwNyAxNTguNjMsNTEuNTMyIDE1OC42Myw1Mi44OTIgQzE1OC42Myw1OS4xMzQgMTU0LjIwNSw2Ni43NjcgMTQ4Ljc2NSw2OS45MDcgQzE0Ny4xOTIsNzAuODE2IDE0NS42ODEsNzEuMjgzIDE0NC4yNzYsNzEuMjgzIEMxNDMuNjM0LDcxLjI4MyAxNDMuMDMzLDcxLjE5MiAxNDIuNDc2LDcxIEwxNDIuNDc2LDcxIFoiIGlkPSJGaWxsLTMzIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0OC42NDgsNjkuNzA0IEMxNTQuMDMyLDY2LjU5NiAxNTguMzk2LDU5LjA2OCAxNTguMzk2LDUyLjg5MSBDMTU4LjM5Niw1MC44MzkgMTU3LjkxMyw0OS4xOTggMTU3LjA3NCw0OC4wMyBDMTU1LjI4OSw0Ni43NzggMTUyLjY5OSw0Ni44MzYgMTQ5LjgxNiw0OC41MDEgQzE0NC40MzMsNTEuNjA5IDE0MC4wNjgsNTkuMTM3IDE0MC4wNjgsNjUuMzE0IEMxNDAuMDY4LDY3LjM2NSAxNDAuNTUyLDY5LjAwNiAxNDEuMzkxLDcwLjE3NCBDMTQzLjE3Niw3MS40MjcgMTQ1Ljc2NSw3MS4zNjkgMTQ4LjY0OCw2OS43MDQiIGlkPSJGaWxsLTM0IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NC4yNzYsNzEuMjc2IEwxNDQuMjc2LDcxLjI3NiBDMTQzLjEzMyw3MS4yNzYgMTQyLjExOCw3MC45NjkgMTQxLjI1Nyw3MC4zNjUgQzE0MS4yMzYsNzAuMzUxIDE0MS4yMTcsNzAuMzMyIDE0MS4yMDIsNzAuMzExIEMxNDAuMzA3LDY5LjA2NyAxMzkuODM1LDY3LjMzOSAxMzkuODM1LDY1LjMxNCBDMTM5LjgzNSw1OS4wNzMgMTQ0LjI2LDUxLjQzOSAxNDkuNyw0OC4yOTggQzE1MS4yNzMsNDcuMzkgMTUyLjc4NCw0Ni45MjkgMTU0LjE4OSw0Ni45MjkgQzE1NS4zMzIsNDYuOTI5IDE1Ni4zNDcsNDcuMjM2IDE1Ny4yMDgsNDcuODM5IEMxNTcuMjI5LDQ3Ljg1NCAxNTcuMjQ4LDQ3Ljg3MyAxNTcuMjYzLDQ3Ljg5NCBDMTU4LjE1Nyw0OS4xMzggMTU4LjYzLDUwLjg2NSAxNTguNjMsNTIuODkxIEMxNTguNjMsNTkuMTMyIDE1NC4yMDUsNjYuNzY2IDE0OC43NjUsNjkuOTA3IEMxNDcuMTkyLDcwLjgxNSAxNDUuNjgxLDcxLjI3NiAxNDQuMjc2LDcxLjI3NiBMMTQ0LjI3Niw3MS4yNzYgWiBNMTQxLjU1OCw3MC4xMDQgQzE0Mi4zMzEsNzAuNjM3IDE0My4yNDUsNzEuMDA1IDE0NC4yNzYsNzEuMDA1IEMxNDUuNTk4LDcxLjAwNSAxNDcuMDMsNzAuNDY3IDE0OC41MzIsNjkuNiBDMTUzLjg0Miw2Ni41MzQgMTU4LjE2Myw1OS4wMzMgMTU4LjE2Myw1Mi45MzkgQzE1OC4xNjMsNTEuMDMxIDE1Ny43MjksNDkuMzg1IDE1Ni45MDcsNDguMjIzIEMxNTYuMTMzLDQ3LjY5MSAxNTUuMjE5LDQ3LjQwOSAxNTQuMTg5LDQ3LjQwOSBDMTUyLjg2Nyw0Ny40MDkgMTUxLjQzNSw0Ny44NDIgMTQ5LjkzMyw0OC43MDkgQzE0NC42MjMsNTEuNzc1IDE0MC4zMDIsNTkuMjczIDE0MC4zMDIsNjUuMzY2IEMxNDAuMzAyLDY3LjI3NiAxNDAuNzM2LDY4Ljk0MiAxNDEuNTU4LDcwLjEwNCBMMTQxLjU1OCw3MC4xMDQgWiIgaWQ9IkZpbGwtMzUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUwLjcyLDY1LjM2MSBMMTUwLjM1Nyw2NS4wNjYgQzE1MS4xNDcsNjQuMDkyIDE1MS44NjksNjMuMDQgMTUyLjUwNSw2MS45MzggQzE1My4zMTMsNjAuNTM5IDE1My45NzgsNTkuMDY3IDE1NC40ODIsNTcuNTYzIEwxNTQuOTI1LDU3LjcxMiBDMTU0LjQxMiw1OS4yNDUgMTUzLjczMyw2MC43NDUgMTUyLjkxLDYyLjE3MiBDMTUyLjI2Miw2My4yOTUgMTUxLjUyNSw2NC4zNjggMTUwLjcyLDY1LjM2MSIgaWQ9IkZpbGwtMzYiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE1LjkxNyw4NC41MTQgTDExNS41NTQsODQuMjIgQzExNi4zNDQsODMuMjQ1IDExNy4wNjYsODIuMTk0IDExNy43MDIsODEuMDkyIEMxMTguNTEsNzkuNjkyIDExOS4xNzUsNzguMjIgMTE5LjY3OCw3Ni43MTcgTDEyMC4xMjEsNzYuODY1IEMxMTkuNjA4LDc4LjM5OCAxMTguOTMsNzkuODk5IDExOC4xMDYsODEuMzI2IEMxMTcuNDU4LDgyLjQ0OCAxMTYuNzIyLDgzLjUyMSAxMTUuOTE3LDg0LjUxNCIgaWQ9IkZpbGwtMzciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE0LDEzMC40NzYgTDExNCwxMzAuMDA4IEwxMTQsNzYuMDUyIEwxMTQsNzUuNTg0IEwxMTQsNzYuMDUyIEwxMTQsMTMwLjAwOCBMMTE0LDEzMC40NzYiIGlkPSJGaWxsLTM4IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYyLjAwMDAwMCwgMC4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTkuODIyLDM3LjQ3NCBDMTkuODM5LDM3LjMzOSAxOS43NDcsMzcuMTk0IDE5LjU1NSwzNy4wODIgQzE5LjIyOCwzNi44OTQgMTguNzI5LDM2Ljg3MiAxOC40NDYsMzcuMDM3IEwxMi40MzQsNDAuNTA4IEMxMi4zMDMsNDAuNTg0IDEyLjI0LDQwLjY4NiAxMi4yNDMsNDAuNzkzIEMxMi4yNDUsNDAuOTI1IDEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQxLjM3MSBMMTIuMjQ1LDQxLjQxNCBMMTIuMjM4LDQxLjU0MiBDOC4xNDgsNDMuODg3IDUuNjQ3LDQ1LjMyMSA1LjY0Nyw0NS4zMjEgQzUuNjQ2LDQ1LjMyMSAzLjU3LDQ2LjM2NyAyLjg2LDUwLjUxMyBDMi44Niw1MC41MTMgMS45NDgsNTcuNDc0IDEuOTYyLDcwLjI1OCBDMS45NzcsODIuODI4IDIuNTY4LDg3LjMyOCAzLjEyOSw5MS42MDkgQzMuMzQ5LDkzLjI5MyA2LjEzLDkzLjczNCA2LjEzLDkzLjczNCBDNi40NjEsOTMuNzc0IDYuODI4LDkzLjcwNyA3LjIxLDkzLjQ4NiBMODIuNDgzLDQ5LjkzNSBDODQuMjkxLDQ4Ljg2NiA4NS4xNSw0Ni4yMTYgODUuNTM5LDQzLjY1MSBDODYuNzUyLDM1LjY2MSA4Ny4yMTQsMTAuNjczIDg1LjI2NCwzLjc3MyBDODUuMDY4LDMuMDggODQuNzU0LDIuNjkgODQuMzk2LDIuNDkxIEw4Mi4zMSwxLjcwMSBDODEuNTgzLDEuNzI5IDgwLjg5NCwyLjE2OCA4MC43NzYsMi4yMzYgQzgwLjYzNiwyLjMxNyA0MS44MDcsMjQuNTg1IDIwLjAzMiwzNy4wNzIgTDE5LjgyMiwzNy40NzQiIGlkPSJGaWxsLTEiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNODIuMzExLDEuNzAxIEw4NC4zOTYsMi40OTEgQzg0Ljc1NCwyLjY5IDg1LjA2OCwzLjA4IDg1LjI2NCwzLjc3MyBDODcuMjEzLDEwLjY3MyA4Ni43NTEsMzUuNjYgODUuNTM5LDQzLjY1MSBDODUuMTQ5LDQ2LjIxNiA4NC4yOSw0OC44NjYgODIuNDgzLDQ5LjkzNSBMNy4yMSw5My40ODYgQzYuODk3LDkzLjY2NyA2LjU5NSw5My43NDQgNi4zMTQsOTMuNzQ0IEw2LjEzMSw5My43MzMgQzYuMTMxLDkzLjczNCAzLjM0OSw5My4yOTMgMy4xMjgsOTEuNjA5IEMyLjU2OCw4Ny4zMjcgMS45NzcsODIuODI4IDEuOTYzLDcwLjI1OCBDMS45NDgsNTcuNDc0IDIuODYsNTAuNTEzIDIuODYsNTAuNTEzIEMzLjU3LDQ2LjM2NyA1LjY0Nyw0NS4zMjEgNS42NDcsNDUuMzIxIEM1LjY0Nyw0NS4zMjEgOC4xNDgsNDMuODg3IDEyLjIzOCw0MS41NDIgTDEyLjI0NSw0MS40MTQgTDEyLjI0NSw0MS4zNzEgQzEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQwLjkyNSAxMi4yNDMsNDAuNzkzIEMxMi4yNCw0MC42ODYgMTIuMzAyLDQwLjU4MyAxMi40MzQsNDAuNTA4IEwxOC40NDYsMzcuMDM2IEMxOC41NzQsMzYuOTYyIDE4Ljc0NiwzNi45MjYgMTguOTI3LDM2LjkyNiBDMTkuMTQ1LDM2LjkyNiAxOS4zNzYsMzYuOTc5IDE5LjU1NCwzNy4wODIgQzE5Ljc0NywzNy4xOTQgMTkuODM5LDM3LjM0IDE5LjgyMiwzNy40NzQgTDIwLjAzMywzNy4wNzIgQzQxLjgwNiwyNC41ODUgODAuNjM2LDIuMzE4IDgwLjc3NywyLjIzNiBDODAuODk0LDIuMTY4IDgxLjU4MywxLjcyOSA4Mi4zMTEsMS43MDEgTTgyLjMxMSwwLjcwNCBMODIuMjcyLDAuNzA1IEM4MS42NTQsMC43MjggODAuOTg5LDAuOTQ5IDgwLjI5OCwxLjM2MSBMODAuMjc3LDEuMzczIEM4MC4xMjksMS40NTggNTkuNzY4LDEzLjEzNSAxOS43NTgsMzYuMDc5IEMxOS41LDM1Ljk4MSAxOS4yMTQsMzUuOTI5IDE4LjkyNywzNS45MjkgQzE4LjU2MiwzNS45MjkgMTguMjIzLDM2LjAxMyAxNy45NDcsMzYuMTczIEwxMS45MzUsMzkuNjQ0IEMxMS40OTMsMzkuODk5IDExLjIzNiw0MC4zMzQgMTEuMjQ2LDQwLjgxIEwxMS4yNDcsNDAuOTYgTDUuMTY3LDQ0LjQ0NyBDNC43OTQsNDQuNjQ2IDIuNjI1LDQ1Ljk3OCAxLjg3Nyw1MC4zNDUgTDEuODcxLDUwLjM4NCBDMS44NjIsNTAuNDU0IDAuOTUxLDU3LjU1NyAwLjk2NSw3MC4yNTkgQzAuOTc5LDgyLjg3OSAxLjU2OCw4Ny4zNzUgMi4xMzcsOTEuNzI0IEwyLjEzOSw5MS43MzkgQzIuNDQ3LDk0LjA5NCA1LjYxNCw5NC42NjIgNS45NzUsOTQuNzE5IEw2LjAwOSw5NC43MjMgQzYuMTEsOTQuNzM2IDYuMjEzLDk0Ljc0MiA2LjMxNCw5NC43NDIgQzYuNzksOTQuNzQyIDcuMjYsOTQuNjEgNy43MSw5NC4zNSBMODIuOTgzLDUwLjc5OCBDODQuNzk0LDQ5LjcyNyA4NS45ODIsNDcuMzc1IDg2LjUyNSw0My44MDEgQzg3LjcxMSwzNS45ODcgODguMjU5LDEwLjcwNSA4Ni4yMjQsMy41MDIgQzg1Ljk3MSwyLjYwOSA4NS41MiwxLjk3NSA4NC44ODEsMS42MiBMODQuNzQ5LDEuNTU4IEw4Mi42NjQsMC43NjkgQzgyLjU1MSwwLjcyNSA4Mi40MzEsMC43MDQgODIuMzExLDAuNzA0IiBpZD0iRmlsbC0yIiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY2LjI2NywxMS41NjUgTDY3Ljc2MiwxMS45OTkgTDExLjQyMyw0NC4zMjUiIGlkPSJGaWxsLTMiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMjAyLDkwLjU0NSBDMTIuMDI5LDkwLjU0NSAxMS44NjIsOTAuNDU1IDExLjc2OSw5MC4yOTUgQzExLjYzMiw5MC4wNTcgMTEuNzEzLDg5Ljc1MiAxMS45NTIsODkuNjE0IEwzMC4zODksNzguOTY5IEMzMC42MjgsNzguODMxIDMwLjkzMyw3OC45MTMgMzEuMDcxLDc5LjE1MiBDMzEuMjA4LDc5LjM5IDMxLjEyNyw3OS42OTYgMzAuODg4LDc5LjgzMyBMMTIuNDUxLDkwLjQ3OCBMMTIuMjAyLDkwLjU0NSIgaWQ9IkZpbGwtNCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMy43NjQsNDIuNjU0IEwxMy42NTYsNDIuNTkyIEwxMy43MDIsNDIuNDIxIEwxOC44MzcsMzkuNDU3IEwxOS4wMDcsMzkuNTAyIEwxOC45NjIsMzkuNjczIEwxMy44MjcsNDIuNjM3IEwxMy43NjQsNDIuNjU0IiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTguNTIsOTAuMzc1IEw4LjUyLDQ2LjQyMSBMOC41ODMsNDYuMzg1IEw3NS44NCw3LjU1NCBMNzUuODQsNTEuNTA4IEw3NS43NzgsNTEuNTQ0IEw4LjUyLDkwLjM3NSBMOC41Miw5MC4zNzUgWiBNOC43Nyw0Ni41NjQgTDguNzcsODkuOTQ0IEw3NS41OTEsNTEuMzY1IEw3NS41OTEsNy45ODUgTDguNzcsNDYuNTY0IEw4Ljc3LDQ2LjU2NCBaIiBpZD0iRmlsbC02IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTI0Ljk4Niw4My4xODIgQzI0Ljc1Niw4My4zMzEgMjQuMzc0LDgzLjU2NiAyNC4xMzcsODMuNzA1IEwxMi42MzIsOTAuNDA2IEMxMi4zOTUsOTAuNTQ1IDEyLjQyNiw5MC42NTggMTIuNyw5MC42NTggTDEzLjI2NSw5MC42NTggQzEzLjU0LDkwLjY1OCAxMy45NTgsOTAuNTQ1IDE0LjE5NSw5MC40MDYgTDI1LjcsODMuNzA1IEMyNS45MzcsODMuNTY2IDI2LjEyOCw4My40NTIgMjYuMTI1LDgzLjQ0OSBDMjYuMTIyLDgzLjQ0NyAyNi4xMTksODMuMjIgMjYuMTE5LDgyLjk0NiBDMjYuMTE5LDgyLjY3MiAyNS45MzEsODIuNTY5IDI1LjcwMSw4Mi43MTkgTDI0Ljk4Niw4My4xODIiIGlkPSJGaWxsLTciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTMuMjY2LDkwLjc4MiBMMTIuNyw5MC43ODIgQzEyLjUsOTAuNzgyIDEyLjM4NCw5MC43MjYgMTIuMzU0LDkwLjYxNiBDMTIuMzI0LDkwLjUwNiAxMi4zOTcsOTAuMzk5IDEyLjU2OSw5MC4yOTkgTDI0LjA3NCw4My41OTcgQzI0LjMxLDgzLjQ1OSAyNC42ODksODMuMjI2IDI0LjkxOCw4My4wNzggTDI1LjYzMyw4Mi42MTQgQzI1LjcyMyw4Mi41NTUgMjUuODEzLDgyLjUyNSAyNS44OTksODIuNTI1IEMyNi4wNzEsODIuNTI1IDI2LjI0NCw4Mi42NTUgMjYuMjQ0LDgyLjk0NiBDMjYuMjQ0LDgzLjE2IDI2LjI0NSw4My4zMDkgMjYuMjQ3LDgzLjM4MyBMMjYuMjUzLDgzLjM4NyBMMjYuMjQ5LDgzLjQ1NiBDMjYuMjQ2LDgzLjUzMSAyNi4yNDYsODMuNTMxIDI1Ljc2Myw4My44MTIgTDE0LjI1OCw5MC41MTQgQzE0LDkwLjY2NSAxMy41NjQsOTAuNzgyIDEzLjI2Niw5MC43ODIgTDEzLjI2Niw5MC43ODIgWiBNMTIuNjY2LDkwLjUzMiBMMTIuNyw5MC41MzMgTDEzLjI2Niw5MC41MzMgQzEzLjUxOCw5MC41MzMgMTMuOTE1LDkwLjQyNSAxNC4xMzIsOTAuMjk5IEwyNS42MzcsODMuNTk3IEMyNS44MDUsODMuNDk5IDI1LjkzMSw4My40MjQgMjUuOTk4LDgzLjM4MyBDMjUuOTk0LDgzLjI5OSAyNS45OTQsODMuMTY1IDI1Ljk5NCw4Mi45NDYgTDI1Ljg5OSw4Mi43NzUgTDI1Ljc2OCw4Mi44MjQgTDI1LjA1NCw4My4yODcgQzI0LjgyMiw4My40MzcgMjQuNDM4LDgzLjY3MyAyNC4yLDgzLjgxMiBMMTIuNjk1LDkwLjUxNCBMMTIuNjY2LDkwLjUzMiBMMTIuNjY2LDkwLjUzMiBaIiBpZD0iRmlsbC04IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEzLjI2Niw4OS44NzEgTDEyLjcsODkuODcxIEMxMi41LDg5Ljg3MSAxMi4zODQsODkuODE1IDEyLjM1NCw4OS43MDUgQzEyLjMyNCw4OS41OTUgMTIuMzk3LDg5LjQ4OCAxMi41NjksODkuMzg4IEwyNC4wNzQsODIuNjg2IEMyNC4zMzIsODIuNTM1IDI0Ljc2OCw4Mi40MTggMjUuMDY3LDgyLjQxOCBMMjUuNjMyLDgyLjQxOCBDMjUuODMyLDgyLjQxOCAyNS45NDgsODIuNDc0IDI1Ljk3OCw4Mi41ODQgQzI2LjAwOCw4Mi42OTQgMjUuOTM1LDgyLjgwMSAyNS43NjMsODIuOTAxIEwxNC4yNTgsODkuNjAzIEMxNCw4OS43NTQgMTMuNTY0LDg5Ljg3MSAxMy4yNjYsODkuODcxIEwxMy4yNjYsODkuODcxIFogTTEyLjY2Niw4OS42MjEgTDEyLjcsODkuNjIyIEwxMy4yNjYsODkuNjIyIEMxMy41MTgsODkuNjIyIDEzLjkxNSw4OS41MTUgMTQuMTMyLDg5LjM4OCBMMjUuNjM3LDgyLjY4NiBMMjUuNjY3LDgyLjY2OCBMMjUuNjMyLDgyLjY2NyBMMjUuMDY3LDgyLjY2NyBDMjQuODE1LDgyLjY2NyAyNC40MTgsODIuNzc1IDI0LjIsODIuOTAxIEwxMi42OTUsODkuNjAzIEwxMi42NjYsODkuNjIxIEwxMi42NjYsODkuNjIxIFoiIGlkPSJGaWxsLTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMzcsOTAuODAxIEwxMi4zNyw4OS41NTQgTDEyLjM3LDkwLjgwMSIgaWQ9IkZpbGwtMTAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNi4xMyw5My45MDEgQzUuMzc5LDkzLjgwOCA0LjgxNiw5My4xNjQgNC42OTEsOTIuNTI1IEMzLjg2LDg4LjI4NyAzLjU0LDgzLjc0MyAzLjUyNiw3MS4xNzMgQzMuNTExLDU4LjM4OSA0LjQyMyw1MS40MjggNC40MjMsNTEuNDI4IEM1LjEzNCw0Ny4yODIgNy4yMSw0Ni4yMzYgNy4yMSw0Ni4yMzYgQzcuMjEsNDYuMjM2IDgxLjY2NywzLjI1IDgyLjA2OSwzLjAxNyBDODIuMjkyLDIuODg4IDg0LjU1NiwxLjQzMyA4NS4yNjQsMy45NCBDODcuMjE0LDEwLjg0IDg2Ljc1MiwzNS44MjcgODUuNTM5LDQzLjgxOCBDODUuMTUsNDYuMzgzIDg0LjI5MSw0OS4wMzMgODIuNDgzLDUwLjEwMSBMNy4yMSw5My42NTMgQzYuODI4LDkzLjg3NCA2LjQ2MSw5My45NDEgNi4xMyw5My45MDEgQzYuMTMsOTMuOTAxIDMuMzQ5LDkzLjQ2IDMuMTI5LDkxLjc3NiBDMi41NjgsODcuNDk1IDEuOTc3LDgyLjk5NSAxLjk2Miw3MC40MjUgQzEuOTQ4LDU3LjY0MSAyLjg2LDUwLjY4IDIuODYsNTAuNjggQzMuNTcsNDYuNTM0IDUuNjQ3LDQ1LjQ4OSA1LjY0Nyw0NS40ODkgQzUuNjQ2LDQ1LjQ4OSA4LjA2NSw0NC4wOTIgMTIuMjQ1LDQxLjY3OSBMMTMuMTE2LDQxLjU2IEwxOS43MTUsMzcuNzMgTDE5Ljc2MSwzNy4yNjkgTDYuMTMsOTMuOTAxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjMxNyw5NC4xNjEgTDYuMTAyLDk0LjE0OCBMNi4xMDEsOTQuMTQ4IEw1Ljg1Nyw5NC4xMDEgQzUuMTM4LDkzLjk0NSAzLjA4NSw5My4zNjUgMi44ODEsOTEuODA5IEMyLjMxMyw4Ny40NjkgMS43MjcsODIuOTk2IDEuNzEzLDcwLjQyNSBDMS42OTksNTcuNzcxIDIuNjA0LDUwLjcxOCAyLjYxMyw1MC42NDggQzMuMzM4LDQ2LjQxNyA1LjQ0NSw0NS4zMSA1LjUzNSw0NS4yNjYgTDEyLjE2Myw0MS40MzkgTDEzLjAzMyw0MS4zMiBMMTkuNDc5LDM3LjU3OCBMMTkuNTEzLDM3LjI0NCBDMTkuNTI2LDM3LjEwNyAxOS42NDcsMzcuMDA4IDE5Ljc4NiwzNy4wMjEgQzE5LjkyMiwzNy4wMzQgMjAuMDIzLDM3LjE1NiAyMC4wMDksMzcuMjkzIEwxOS45NSwzNy44ODIgTDEzLjE5OCw0MS44MDEgTDEyLjMyOCw0MS45MTkgTDUuNzcyLDQ1LjcwNCBDNS43NDEsNDUuNzIgMy43ODIsNDYuNzcyIDMuMTA2LDUwLjcyMiBDMy4wOTksNTAuNzgyIDIuMTk4LDU3LjgwOCAyLjIxMiw3MC40MjQgQzIuMjI2LDgyLjk2MyAyLjgwOSw4Ny40MiAzLjM3Myw5MS43MjkgQzMuNDY0LDkyLjQyIDQuMDYyLDkyLjg4MyA0LjY4Miw5My4xODEgQzQuNTY2LDkyLjk4NCA0LjQ4Niw5Mi43NzYgNC40NDYsOTIuNTcyIEMzLjY2NSw4OC41ODggMy4yOTEsODQuMzcgMy4yNzYsNzEuMTczIEMzLjI2Miw1OC41MiA0LjE2Nyw1MS40NjYgNC4xNzYsNTEuMzk2IEM0LjkwMSw0Ny4xNjUgNy4wMDgsNDYuMDU5IDcuMDk4LDQ2LjAxNCBDNy4wOTQsNDYuMDE1IDgxLjU0MiwzLjAzNCA4MS45NDQsMi44MDIgTDgxLjk3MiwyLjc4NSBDODIuODc2LDIuMjQ3IDgzLjY5MiwyLjA5NyA4NC4zMzIsMi4zNTIgQzg0Ljg4NywyLjU3MyA4NS4yODEsMy4wODUgODUuNTA0LDMuODcyIEM4Ny41MTgsMTEgODYuOTY0LDM2LjA5MSA4NS43ODUsNDMuODU1IEM4NS4yNzgsNDcuMTk2IDg0LjIxLDQ5LjM3IDgyLjYxLDUwLjMxNyBMNy4zMzUsOTMuODY5IEM2Ljk5OSw5NC4wNjMgNi42NTgsOTQuMTYxIDYuMzE3LDk0LjE2MSBMNi4zMTcsOTQuMTYxIFogTTYuMTcsOTMuNjU0IEM2LjQ2Myw5My42OSA2Ljc3NCw5My42MTcgNy4wODUsOTMuNDM3IEw4Mi4zNTgsNDkuODg2IEM4NC4xODEsNDguODA4IDg0Ljk2LDQ1Ljk3MSA4NS4yOTIsNDMuNzggQzg2LjQ2NiwzNi4wNDkgODcuMDIzLDExLjA4NSA4NS4wMjQsNC4wMDggQzg0Ljg0NiwzLjM3NyA4NC41NTEsMi45NzYgODQuMTQ4LDIuODE2IEM4My42NjQsMi42MjMgODIuOTgyLDIuNzY0IDgyLjIyNywzLjIxMyBMODIuMTkzLDMuMjM0IEM4MS43OTEsMy40NjYgNy4zMzUsNDYuNDUyIDcuMzM1LDQ2LjQ1MiBDNy4zMDQsNDYuNDY5IDUuMzQ2LDQ3LjUyMSA0LjY2OSw1MS40NzEgQzQuNjYyLDUxLjUzIDMuNzYxLDU4LjU1NiAzLjc3NSw3MS4xNzMgQzMuNzksODQuMzI4IDQuMTYxLDg4LjUyNCA0LjkzNiw5Mi40NzYgQzUuMDI2LDkyLjkzNyA1LjQxMiw5My40NTkgNS45NzMsOTMuNjE1IEM2LjA4Nyw5My42NCA2LjE1OCw5My42NTIgNi4xNjksOTMuNjU0IEw2LjE3LDkzLjY1NCBMNi4xNyw5My42NTQgWiIgaWQ9IkZpbGwtMTIiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4zMTcsNjguOTgyIEM3LjgwNiw2OC43MDEgOC4yMDIsNjguOTI2IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNi44MjksNzEuMjk0IDYuNDMzLDcxLjA2OSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIiBpZD0iRmlsbC0xMyIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjkyLDcxLjEzMyBDNi42MzEsNzEuMTMzIDYuNDMzLDcwLjkwNSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIEM3LjQ2LDY4LjkgNy41OTUsNjguODYxIDcuNzE0LDY4Ljg2MSBDOC4wMDMsNjguODYxIDguMjAyLDY5LjA5IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNy4xNzQsNzEuMDk0IDcuMDM5LDcxLjEzMyA2LjkyLDcxLjEzMyBNNy43MTQsNjguNjc0IEM3LjU1Nyw2OC42NzQgNy4zOTIsNjguNzIzIDcuMjI0LDY4LjgyMSBDNi42NzYsNjkuMTM4IDYuMjQ2LDY5Ljg3OSA2LjI0Niw3MC41MDggQzYuMjQ2LDcwLjk5NCA2LjUxNyw3MS4zMiA2LjkyLDcxLjMyIEM3LjA3OCw3MS4zMiA3LjI0Myw3MS4yNzEgNy40MTEsNzEuMTc0IEM3Ljk1OSw3MC44NTcgOC4zODksNzAuMTE3IDguMzg5LDY5LjQ4NyBDOC4zODksNjkuMDAxIDguMTE3LDY4LjY3NCA3LjcxNCw2OC42NzQiIGlkPSJGaWxsLTE0IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYuOTIsNzAuOTQ3IEM2LjY0OSw3MC45NDcgNi42MjEsNzAuNjQgNi42MjEsNzAuNTA4IEM2LjYyMSw3MC4wMTcgNi45ODIsNjkuMzkyIDcuNDExLDY5LjE0NSBDNy41MjEsNjkuMDgyIDcuNjI1LDY5LjA0OSA3LjcxNCw2OS4wNDkgQzcuOTg2LDY5LjA0OSA4LjAxNSw2OS4zNTUgOC4wMTUsNjkuNDg3IEM4LjAxNSw2OS45NzggNy42NTIsNzAuNjAzIDcuMjI0LDcwLjg1MSBDNy4xMTUsNzAuOTE0IDcuMDEsNzAuOTQ3IDYuOTIsNzAuOTQ3IE03LjcxNCw2OC44NjEgQzcuNTk1LDY4Ljg2MSA3LjQ2LDY4LjkgNy4zMTcsNjguOTgyIEM2LjgyOSw2OS4yNjUgNi40MzMsNjkuOTQ4IDYuNDMzLDcwLjUwOCBDNi40MzMsNzAuOTA1IDYuNjMxLDcxLjEzMyA2LjkyLDcxLjEzMyBDNy4wMzksNzEuMTMzIDcuMTc0LDcxLjA5NCA3LjMxNyw3MS4wMTIgQzcuODA2LDcwLjczIDguMjAyLDcwLjA0NyA4LjIwMiw2OS40ODcgQzguMjAyLDY5LjA5IDguMDAzLDY4Ljg2MSA3LjcxNCw2OC44NjEiIGlkPSJGaWxsLTE1IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTcuNDQ0LDg1LjM1IEM3LjcwOCw4NS4xOTggNy45MjEsODUuMzE5IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuOTI1IDcuNzA4LDg2LjI5MiA3LjQ0NCw4Ni40NDQgQzcuMTgxLDg2LjU5NyA2Ljk2Nyw4Ni40NzUgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IiBpZD0iRmlsbC0xNiIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik03LjIzLDg2LjUxIEM3LjA3NCw4Ni41MSA2Ljk2Nyw4Ni4zODcgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IEM3LjUyMSw4NS4zMDUgNy41OTQsODUuMjg0IDcuNjU4LDg1LjI4NCBDNy44MTQsODUuMjg0IDcuOTIxLDg1LjQwOCA3LjkyMSw4NS42MjIgQzcuOTIxLDg1LjkyNSA3LjcwOCw4Ni4yOTIgNy40NDQsODYuNDQ0IEM3LjM2Nyw4Ni40ODkgNy4yOTQsODYuNTEgNy4yMyw4Ni41MSBNNy42NTgsODUuMDk4IEM3LjU1OCw4NS4wOTggNy40NTUsODUuMTI3IDcuMzUxLDg1LjE4OCBDNy4wMzEsODUuMzczIDYuNzgxLDg1LjgwNiA2Ljc4MSw4Ni4xNzMgQzYuNzgxLDg2LjQ4MiA2Ljk2Niw4Ni42OTcgNy4yMyw4Ni42OTcgQzcuMzMsODYuNjk3IDcuNDMzLDg2LjY2NiA3LjUzOCw4Ni42MDcgQzcuODU4LDg2LjQyMiA4LjEwOCw4NS45ODkgOC4xMDgsODUuNjIyIEM4LjEwOCw4NS4zMTMgNy45MjMsODUuMDk4IDcuNjU4LDg1LjA5OCIgaWQ9IkZpbGwtMTciIGZpbGw9IiM4MDk3QTIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4yMyw4Ni4zMjIgTDcuMTU0LDg2LjE3MyBDNy4xNTQsODUuOTM4IDcuMzMzLDg1LjYyOSA3LjUzOCw4NS41MTIgTDcuNjU4LDg1LjQ3MSBMNy43MzQsODUuNjIyIEM3LjczNCw4NS44NTYgNy41NTUsODYuMTY0IDcuMzUxLDg2LjI4MiBMNy4yMyw4Ni4zMjIgTTcuNjU4LDg1LjI4NCBDNy41OTQsODUuMjg0IDcuNTIxLDg1LjMwNSA3LjQ0NCw4NS4zNSBDNy4xODEsODUuNTAyIDYuOTY3LDg1Ljg3MSA2Ljk2Nyw4Ni4xNzMgQzYuOTY3LDg2LjM4NyA3LjA3NCw4Ni41MSA3LjIzLDg2LjUxIEM3LjI5NCw4Ni41MSA3LjM2Nyw4Ni40ODkgNy40NDQsODYuNDQ0IEM3LjcwOCw4Ni4yOTIgNy45MjEsODUuOTI1IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuNDA4IDcuODE0LDg1LjI4NCA3LjY1OCw4NS4yODQiIGlkPSJGaWxsLTE4IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTc3LjI3OCw3Ljc2OSBMNzcuMjc4LDUxLjQzNiBMMTAuMjA4LDkwLjE2IEwxMC4yMDgsNDYuNDkzIEw3Ny4yNzgsNy43NjkiIGlkPSJGaWxsLTE5IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEwLjA4Myw5MC4zNzUgTDEwLjA4Myw0Ni40MjEgTDEwLjE0Niw0Ni4zODUgTDc3LjQwMyw3LjU1NCBMNzcuNDAzLDUxLjUwOCBMNzcuMzQxLDUxLjU0NCBMMTAuMDgzLDkwLjM3NSBMMTAuMDgzLDkwLjM3NSBaIE0xMC4zMzMsNDYuNTY0IEwxMC4zMzMsODkuOTQ0IEw3Ny4xNTQsNTEuMzY1IEw3Ny4xNTQsNy45ODUgTDEwLjMzMyw0Ni41NjQgTDEwLjMzMyw0Ni41NjQgWiIgaWQ9IkZpbGwtMjAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMjUuNzM3LDg4LjY0NyBMMTE4LjA5OCw5MS45ODEgTDExOC4wOTgsODQgTDEwNi42MzksODguNzEzIEwxMDYuNjM5LDk2Ljk4MiBMOTksMTAwLjMxNSBMMTEyLjM2OSwxMDMuOTYxIEwxMjUuNzM3LDg4LjY0NyIgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTIiIGZpbGw9IiM0NTVBNjQiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+');
+};
+
+module.exports = RotateInstructions;
+
+},{"./util.js":13}],13:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var Util = {};
+
+Util.base64 = function(mimeType, base64) {
+ return 'data:' + mimeType + ';base64,' + base64;
+};
+
+Util.isMobile = function() {
+ var check = false;
+ (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
+ return check;
+};
+
+Util.isFirefox = function() {
+ return /firefox/i.test(navigator.userAgent);
+};
+
+Util.isIOS = function() {
+ return /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
+};
+
+Util.isIFrame = function() {
+ try {
+ return window.self !== window.top;
+ } catch (e) {
+ return true;
+ }
+};
+
+Util.appendQueryParameter = function(url, key, value) {
+ // Determine delimiter based on if the URL already GET parameters in it.
+ var delimiter = (url.indexOf('?') < 0 ? '?' : '&');
+ url += delimiter + key + '=' + value;
+ return url;
+};
+
+// From http://goo.gl/4WX3tg
+Util.getQueryParameter = function(name) {
+ name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
+ var regex = new RegExp("[\\?&]" + name + "=([^]*)"),
+ results = regex.exec(location.search);
+ return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
+};
+
+Util.isLandscapeMode = function() {
+ return (window.orientation == 90 || window.orientation == -90);
+};
+
+Util.getScreenWidth = function() {
+ return Math.max(window.screen.width, window.screen.height) *
+ window.devicePixelRatio;
+};
+
+Util.getScreenHeight = function() {
+ return Math.min(window.screen.width, window.screen.height) *
+ window.devicePixelRatio;
+};
+
+/**
+ * Utility to convert the projection matrix to a vector accepted by the shader.
+ *
+ * @param {Object} opt_params A rectangle to scale this vector by.
+ */
+Util.projectionMatrixToVector_ = function(matrix, opt_params) {
+ var params = opt_params || {};
+ var xScale = params.xScale || 1;
+ var yScale = params.yScale || 1;
+ var xTrans = params.xTrans || 0;
+ var yTrans = params.yTrans || 0;
+
+ var elements = matrix.elements;
+ var vec = new THREE.Vector4();
+ vec.set(elements[4*0 + 0] * xScale,
+ elements[4*1 + 1] * yScale,
+ elements[4*2 + 0] - 1 - xTrans,
+ elements[4*2 + 1] - 1 - yTrans).divideScalar(2);
+ return vec;
+};
+
+Util.leftProjectionVectorToRight_ = function(left) {
+ //projectionLeft + vec4(0.0, 0.0, 1.0, 0.0)) * vec4(1.0, 1.0, -1.0, 1.0);
+ var out = new THREE.Vector4(0, 0, 1, 0);
+ out.add(left); // out = left + (0, 0, 1, 0).
+ out.z *= -1; // Flip z.
+
+ return out;
+};
+
+module.exports = Util;
+
+},{}],14:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var Emitter = _dereq_('./emitter.js');
+var Util = _dereq_('./util.js');
+
+var DEFAULT_VIEWER = 'CardboardV1';
+var VIEWER_KEY = 'WEBVR_CARDBOARD_VIEWER';
+
+/**
+ * Creates a viewer selector with the options specified. Supports being shown
+ * and hidden. Generates events when viewer parameters change. Also supports
+ * saving the currently selected index in localStorage.
+ *
+ * @param {Object} options Option labels for all valid selections {name: index}.
+ */
+function ViewerSelector(options) {
+ // Try to load the selected key from local storage. If none exists, use the
+ // default key.
+ try {
+ this.selectedKey = localStorage.getItem(VIEWER_KEY) || DEFAULT_VIEWER;
+ } catch(error) {
+ console.error('Failed to load viewer profile: %s', error);
+ }
+ this.dialog = this.createDialog_(options);
+ this.options = options;
+ document.body.appendChild(this.dialog);
+}
+ViewerSelector.prototype = new Emitter();
+
+ViewerSelector.prototype.show = function() {
+ //console.log('ViewerSelector.show');
+
+ // Ensure the currently selected item is checked.
+ var selected = this.dialog.querySelector('#' + this.selectedKey);
+ selected.checked = true;
+
+ // Show the UI.
+ this.dialog.style.display = 'block';
+};
+
+ViewerSelector.prototype.hide = function() {
+ //console.log('ViewerSelector.hide');
+ this.dialog.style.display = 'none';
+};
+
+ViewerSelector.prototype.getSelectedKey_ = function() {
+ var input = this.dialog.querySelector('input[name=field]:checked');
+ if (input) {
+ return input.id;
+ }
+ return null;
+};
+
+ViewerSelector.prototype.onSave_ = function() {
+ this.selectedKey = this.getSelectedKey_();
+ if (!this.selectedKey || !this.options[this.selectedKey]) {
+ console.error('ViewerSelector.onSave_: this should never happen!');
+ return;
+ }
+
+ this.emit('change', this.options[this.selectedKey]);
+
+ // Attempt to save the viewer profile, but fails in private mode.
+ try {
+ localStorage.setItem(VIEWER_KEY, this.selectedKey);
+ } catch(error) {
+ console.error('Failed to save viewer profile: %s', error);
+ }
+ this.hide();
+};
+
+/**
+ * Creates the dialog.
+ */
+ViewerSelector.prototype.createDialog_ = function(options) {
+ var container = document.createElement('div');
+ container.style.display = 'none';
+ // Create an overlay that dims the background, and which goes away when you
+ // tap it.
+ var overlay = document.createElement('div');
+ var s = overlay.style;
+ s.position = 'fixed';
+ s.left = 0;
+ s.top = 0;
+ s.width = '100%';
+ s.height = '100%';
+ s.background = 'rgba(0, 0, 0, 0.3)';
+ overlay.addEventListener('click', this.hide.bind(this));
+
+ var width = 280;
+ var dialog = document.createElement('div');
+ var s = dialog.style;
+ s.boxSizing = 'border-box';
+ s.position = 'fixed';
+ s.top = '24px';
+ s.left = '50%';
+ s.marginLeft = (-width/2) + 'px';
+ s.width = width + 'px';
+ s.padding = '24px';
+ s.overflow = 'hidden';
+ s.background = '#fafafa';
+ s.fontFamily = "'Roboto', sans-serif";
+ s.boxShadow = '0px 5px 20px #666';
+
+ dialog.appendChild(this.createH1_('Select your viewer'));
+ for (var id in options) {
+ dialog.appendChild(this.createChoice_(id, options[id].label));
+ }
+ dialog.appendChild(this.createButton_('Save', this.onSave_.bind(this)));
+
+ container.appendChild(overlay);
+ container.appendChild(dialog);
+
+ return container;
+};
+
+ViewerSelector.prototype.createH1_ = function(name) {
+ var h1 = document.createElement('h1');
+ var s = h1.style;
+ s.color = 'black';
+ s.fontSize = '20px';
+ s.fontWeight = 'bold';
+ s.marginTop = 0;
+ s.marginBottom = '24px';
+ h1.innerHTML = name;
+ return h1;
+};
+
+ViewerSelector.prototype.createChoice_ = function(id, name) {
+ /*
+
+
+
+
+ */
+ var div = document.createElement('div');
+ div.style.marginTop = '8px';
+ div.style.color = 'black';
+
+ var input = document.createElement('input');
+ input.style.fontSize = '30px';
+ input.setAttribute('id', id);
+ input.setAttribute('type', 'radio');
+ input.setAttribute('value', id);
+ input.setAttribute('name', 'field');
+
+ var label = document.createElement('label');
+ label.style.marginLeft = '4px';
+ label.setAttribute('for', id);
+ label.innerHTML = name;
+
+ div.appendChild(input);
+ div.appendChild(label);
+
+ return div;
+};
+
+ViewerSelector.prototype.createButton_ = function(label, onclick) {
+ var button = document.createElement('button');
+ button.innerHTML = label;
+ var s = button.style;
+ s.float = 'right';
+ s.textTransform = 'uppercase';
+ s.color = '#1094f7';
+ s.fontSize = '14px';
+ s.letterSpacing = 0;
+ s.border = 0;
+ s.background = 'none';
+ s.marginTop = '16px';
+
+ button.addEventListener('click', onclick);
+
+ return button;
+};
+
+module.exports = ViewerSelector;
+
+},{"./emitter.js":9,"./util.js":13}],15:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var Util = _dereq_('./util.js');
+
+/**
+ * Android and iOS compatible wakelock implementation.
+ *
+ * Refactored thanks to dkovalev@.
+ */
+function AndroidWakeLock() {
+ var video = document.createElement('video');
+
+ video.addEventListener('ended', function() {
+ video.play();
+ });
+
+ this.request = function() {
+ if (video.paused) {
+ // Base64 version of videos_src/no-sleep-120s.mp4.
+ video.src = Util.base64('video/mp4', 'AAAAGGZ0eXBpc29tAAAAAG1wNDFhdmMxAAAIA21vb3YAAABsbXZoZAAAAADSa9v60mvb+gABX5AAlw/gAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAdkdHJhawAAAFx0a2hkAAAAAdJr2/rSa9v6AAAAAQAAAAAAlw/gAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAQAAAAHAAAAAAAJGVkdHMAAAAcZWxzdAAAAAAAAAABAJcP4AAAAAAAAQAAAAAG3G1kaWEAAAAgbWRoZAAAAADSa9v60mvb+gAPQkAGjneAFccAAAAAAC1oZGxyAAAAAAAAAAB2aWRlAAAAAAAAAAAAAAAAVmlkZW9IYW5kbGVyAAAABodtaW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAZHc3RibAAAAJdzdHNkAAAAAAAAAAEAAACHYXZjMQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAMABwASAAAAEgAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABj//wAAADFhdmNDAWQAC//hABlnZAALrNlfllw4QAAAAwBAAAADAKPFCmWAAQAFaOvssiwAAAAYc3R0cwAAAAAAAAABAAAAbgAPQkAAAAAUc3RzcwAAAAAAAAABAAAAAQAAA4BjdHRzAAAAAAAAAG4AAAABAD0JAAAAAAEAehIAAAAAAQA9CQAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEALcbAAAAAHHN0c2MAAAAAAAAAAQAAAAEAAABuAAAAAQAAAcxzdHN6AAAAAAAAAAAAAABuAAADCQAAABgAAAAOAAAADgAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABMAAAAUc3RjbwAAAAAAAAABAAAIKwAAACt1ZHRhAAAAI6llbmMAFwAAdmxjIDIuMi4xIHN0cmVhbSBvdXRwdXQAAAAId2lkZQAACRRtZGF0AAACrgX//6vcRem95tlIt5Ys2CDZI+7veDI2NCAtIGNvcmUgMTQyIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MzoweDEzIG1lPWhleCBzdWJtZT03IHBzeT0xIHBzeV9yZD0xLjAwOjAuMDAgbWl4ZWRfcmVmPTEgbWVfcmFuZ2U9MTYgY2hyb21hX21lPTEgdHJlbGxpcz0xIDh4OGRjdD0xIGNxbT0wIGRlYWR6b25lPTIxLDExIGZhc3RfcHNraXA9MSBjaHJvbWFfcXBfb2Zmc2V0PS0yIHRocmVhZHM9MTIgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MSBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTEgb3Blbl9nb3A9MCB3ZWlnaHRwPTIga2V5aW50PTI1MCBrZXlpbnRfbWluPTEgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD00MCByYz1hYnIgbWJ0cmVlPTEgYml0cmF0ZT0xMDAgcmF0ZXRvbD0xLjAgcWNvbXA9MC42MCBxcG1pbj0xMCBxcG1heD01MSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAU2WIhAAQ/8ltlOe+cTZuGkKg+aRtuivcDZ0pBsfsEi9p/i1yU9DxS2lq4dXTinViF1URBKXgnzKBd/Uh1bkhHtMrwrRcOJslD01UB+fyaL6ef+DBAAAAFEGaJGxBD5B+v+a+4QqF3MgBXz9MAAAACkGeQniH/+94r6EAAAAKAZ5hdEN/8QytwAAAAAgBnmNqQ3/EgQAAAA5BmmhJqEFomUwIIf/+4QAAAApBnoZFESw//76BAAAACAGepXRDf8SBAAAACAGep2pDf8SAAAAADkGarEmoQWyZTAgh//7gAAAACkGeykUVLD//voEAAAAIAZ7pdEN/xIAAAAAIAZ7rakN/xIAAAAAOQZrwSahBbJlMCCH//uEAAAAKQZ8ORRUsP/++gQAAAAgBny10Q3/EgQAAAAgBny9qQ3/EgAAAAA5BmzRJqEFsmUwIIf/+4AAAAApBn1JFFSw//76BAAAACAGfcXRDf8SAAAAACAGfc2pDf8SAAAAADkGbeEmoQWyZTAgh//7hAAAACkGflkUVLD//voAAAAAIAZ+1dEN/xIEAAAAIAZ+3akN/xIEAAAAOQZu8SahBbJlMCCH//uAAAAAKQZ/aRRUsP/++gQAAAAgBn/l0Q3/EgAAAAAgBn/tqQ3/EgQAAAA5Bm+BJqEFsmUwIIf/+4QAAAApBnh5FFSw//76AAAAACAGePXRDf8SAAAAACAGeP2pDf8SBAAAADkGaJEmoQWyZTAgh//7gAAAACkGeQkUVLD//voEAAAAIAZ5hdEN/xIAAAAAIAZ5jakN/xIEAAAAOQZpoSahBbJlMCCH//uEAAAAKQZ6GRRUsP/++gQAAAAgBnqV0Q3/EgQAAAAgBnqdqQ3/EgAAAAA5BmqxJqEFsmUwIIf/+4AAAAApBnspFFSw//76BAAAACAGe6XRDf8SAAAAACAGe62pDf8SAAAAADkGa8EmoQWyZTAgh//7hAAAACkGfDkUVLD//voEAAAAIAZ8tdEN/xIEAAAAIAZ8vakN/xIAAAAAOQZs0SahBbJlMCCH//uAAAAAKQZ9SRRUsP/++gQAAAAgBn3F0Q3/EgAAAAAgBn3NqQ3/EgAAAAA5Bm3hJqEFsmUwIIf/+4QAAAApBn5ZFFSw//76AAAAACAGftXRDf8SBAAAACAGft2pDf8SBAAAADkGbvEmoQWyZTAgh//7gAAAACkGf2kUVLD//voEAAAAIAZ/5dEN/xIAAAAAIAZ/7akN/xIEAAAAOQZvgSahBbJlMCCH//uEAAAAKQZ4eRRUsP/++gAAAAAgBnj10Q3/EgAAAAAgBnj9qQ3/EgQAAAA5BmiRJqEFsmUwIIf/+4AAAAApBnkJFFSw//76BAAAACAGeYXRDf8SAAAAACAGeY2pDf8SBAAAADkGaaEmoQWyZTAgh//7hAAAACkGehkUVLD//voEAAAAIAZ6ldEN/xIEAAAAIAZ6nakN/xIAAAAAOQZqsSahBbJlMCCH//uAAAAAKQZ7KRRUsP/++gQAAAAgBnul0Q3/EgAAAAAgBnutqQ3/EgAAAAA5BmvBJqEFsmUwIIf/+4QAAAApBnw5FFSw//76BAAAACAGfLXRDf8SBAAAACAGfL2pDf8SAAAAADkGbNEmoQWyZTAgh//7gAAAACkGfUkUVLD//voEAAAAIAZ9xdEN/xIAAAAAIAZ9zakN/xIAAAAAOQZt4SahBbJlMCCH//uEAAAAKQZ+WRRUsP/++gAAAAAgBn7V0Q3/EgQAAAAgBn7dqQ3/EgQAAAA5Bm7xJqEFsmUwIIf/+4AAAAApBn9pFFSw//76BAAAACAGf+XRDf8SAAAAACAGf+2pDf8SBAAAADkGb4EmoQWyZTAgh//7hAAAACkGeHkUVLD//voAAAAAIAZ49dEN/xIAAAAAIAZ4/akN/xIEAAAAOQZokSahBbJlMCCH//uAAAAAKQZ5CRRUsP/++gQAAAAgBnmF0Q3/EgAAAAAgBnmNqQ3/EgQAAAA5BmmhJqEFsmUwIIf/+4QAAAApBnoZFFSw//76BAAAACAGepXRDf8SBAAAACAGep2pDf8SAAAAADkGarEmoQWyZTAgh//7gAAAACkGeykUVLD//voEAAAAIAZ7pdEN/xIAAAAAIAZ7rakN/xIAAAAAPQZruSahBbJlMFEw3//7B');
+ video.play();
+ }
+ };
+
+ this.release = function() {
+ video.pause();
+ video.src = '';
+ };
+}
+
+function iOSWakeLock() {
+ var timer = null;
+
+ this.request = function() {
+ if (!timer) {
+ timer = setInterval(function() {
+ window.location = window.location;
+ setTimeout(window.stop, 0);
+ }, 30000);
+ }
+ }
+
+ this.release = function() {
+ if (timer) {
+ clearInterval(timer);
+ timer = null;
+ }
+ }
+}
+
+
+function getWakeLock() {
+ var userAgent = navigator.userAgent || navigator.vendor || window.opera;
+ if (userAgent.match(/iPhone/i) || userAgent.match(/iPod/i)) {
+ return iOSWakeLock;
+ } else {
+ return AndroidWakeLock;
+ }
+}
+
+module.exports = getWakeLock();
+
+},{"./util.js":13}],16:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var ButtonManager = _dereq_('./button-manager.js');
+var CardboardDistorter = _dereq_('./cardboard-distorter.js');
+var DeviceInfo = _dereq_('./device-info.js');
+var Dpdb = _dereq_('./dpdb.js');
+var Emitter = _dereq_('./emitter.js');
+var Modes = _dereq_('./modes.js');
+var RotateInstructions = _dereq_('./rotate-instructions.js');
+var Util = _dereq_('./util.js');
+var ViewerSelector = _dereq_('./viewer-selector.js');
+var Wakelock = _dereq_('./wakelock.js');
+
+/**
+ * Helper for getting in and out of VR mode.
+ * Here we assume VR mode == full screen mode.
+ *
+ * 1. Detects whether or not VR mode is possible by feature detecting for
+ * WebVR (or polyfill).
+ *
+ * 2. If WebVR is available, shows a button that lets you enter VR mode.
+ *
+ * 3. Provides Cardboard-style distortion if the webvr-polyfill is being used.
+ *
+ * 4. Provides best practices while in VR mode.
+ * - Full screen
+ * - Wake lock
+ * - Orientation lock (mobile only)
+ */
+function WebVRManager(renderer, effect, params) {
+ this.params = params || {};
+
+ this.mode = Modes.UNKNOWN;
+
+ // Set option to hide the button.
+ this.hideButton = this.params.hideButton || false;
+ // Whether or not the FOV should be distorted or un-distorted. By default, it
+ // should be distorted, but in the case of vertex shader based distortion,
+ // ensure that we use undistorted parameters.
+ this.isUndistorted = !!this.params.isUndistorted;
+
+ // Save the THREE.js renderer and effect for later.
+ this.renderer = renderer;
+ this.effect = effect;
+ this.button = new ButtonManager();
+ this.rotateInstructions = new RotateInstructions();
+ this.viewerSelector = new ViewerSelector(DeviceInfo.Viewers);
+
+ // Load the DPDB.
+ var shouldFetch = !WebVRConfig.NO_DPDB_FETCH;
+ this.dpdb = new Dpdb(shouldFetch, this.onDeviceParamsUpdated_.bind(this));
+
+ // Create device info and set the correct default viewer.
+ this.deviceInfo = new DeviceInfo(this.dpdb.getDeviceParams());
+ this.deviceInfo.viewer = DeviceInfo.Viewers[this.viewerSelector.selectedKey];
+ console.log('Using the %s viewer.', this.getViewer().label);
+
+ this.distorter = new CardboardDistorter(renderer);
+ this.distorter.updateDeviceInfo(this.deviceInfo);
+
+ this.isVRCompatible = false;
+ this.isFullscreenDisabled = !!Util.getQueryParameter('no_fullscreen');
+ this.startMode = Modes.NORMAL;
+ var startModeParam = parseInt(Util.getQueryParameter('start_mode'));
+ if (!isNaN(startModeParam)) {
+ this.startMode = startModeParam;
+ }
+
+ // Set the correct viewer profile, but only if this is Cardboard.
+ if (Util.isMobile()) {
+ this.onViewerChanged_(this.getViewer());
+ }
+ // Listen for changes to the viewer.
+ this.viewerSelector.on('change', this.onViewerChanged_.bind(this));
+
+ if (this.hideButton) {
+ this.button.setVisibility(false);
+ }
+
+ // Check if the browser is compatible with WebVR.
+ this.getDeviceByType_(HMDVRDevice).then(function(hmd) {
+ // Activate either VR or Immersive mode.
+ if (WebVRConfig.FORCE_DISTORTION) {
+ this.distorter.setActive(true);
+ this.isVRCompatible = true;
+ } else if (hmd) {
+ this.isVRCompatible = true;
+ // Only enable distortion if we are dealing using the polyfill, we have a
+ // perfect device match, and it's not prevented via configuration.
+ if (hmd.deviceName.indexOf('webvr-polyfill') == 0 && this.deviceInfo.getDevice() &&
+ !WebVRConfig.PREVENT_DISTORTION) {
+ this.distorter.setActive(true);
+ }
+ this.hmd = hmd;
+ }
+ // Set the right mode.
+ switch (this.startMode) {
+ case Modes.MAGIC_WINDOW:
+ this.normalToMagicWindow_();
+ this.setMode_(Modes.MAGIC_WINDOW);
+ break;
+ case Modes.VR:
+ this.anyModeToVR_();
+ this.setMode_(Modes.VR);
+ break;
+ default:
+ this.setMode_(Modes.NORMAL);
+ }
+ this.button.on('fs', this.onFSClick_.bind(this));
+ this.button.on('vr', this.onVRClick_.bind(this));
+ this.button.on('back', this.onBackClick_.bind(this));
+ this.button.on('settings', this.onSettingsClick_.bind(this));
+ this.emit('initialized');
+ }.bind(this));
+
+ // Save the input device for later sending timing data.
+ this.getDeviceByType_(PositionSensorVRDevice).then(function(input) {
+ this.input = input;
+ }.bind(this));
+
+ // Whenever we enter fullscreen, we are entering VR or immersive mode.
+ document.addEventListener('webkitfullscreenchange',
+ this.onFullscreenChange_.bind(this));
+ document.addEventListener('mozfullscreenchange',
+ this.onFullscreenChange_.bind(this));
+ document.addEventListener('MSFullscreenChange',
+ this.onFullscreenChange_.bind(this));
+ window.addEventListener('orientationchange',
+ this.onOrientationChange_.bind(this));
+
+ // Create the necessary elements for wake lock to work.
+ this.wakelock = new Wakelock();
+
+ // Save whether or not we want the touch panner to be enabled or disabled by
+ // default.
+ this.isTouchPannerEnabled = !WebVRConfig.TOUCH_PANNER_DISABLED;
+
+}
+
+WebVRManager.prototype = new Emitter();
+
+// Expose these values externally.
+WebVRManager.Modes = Modes;
+
+/**
+ * Promise returns true if there is at least one HMD device available.
+ */
+WebVRManager.prototype.getDeviceByType_ = function(type) {
+ return new Promise(function(resolve, reject) {
+ navigator.getVRDevices().then(function(devices) {
+ // Promise succeeds, but check if there are any devices actually.
+ for (var i = 0; i < devices.length; i++) {
+ if (devices[i] instanceof type) {
+ resolve(devices[i]);
+ break;
+ }
+ }
+ resolve(null);
+ }, function() {
+ // No devices are found.
+ resolve(null);
+ });
+ });
+};
+
+WebVRManager.prototype.isVRMode = function() {
+ return this.mode == Modes.VR;
+};
+
+WebVRManager.prototype.getViewer = function() {
+ return this.deviceInfo.viewer;
+};
+
+WebVRManager.prototype.getDevice = function() {
+ return this.deviceInfo.device;
+};
+
+WebVRManager.prototype.getDeviceInfo = function() {
+ return this.deviceInfo;
+};
+
+WebVRManager.prototype.render = function(scene, camera, timestamp) {
+ this.camera = camera;
+
+ this.resizeIfNeeded_(camera);
+
+ if (this.isVRMode()) {
+ this.distorter.preRender();
+ this.effect.render(scene, camera);
+ this.distorter.postRender();
+ } else {
+ // Scene may be an array of two scenes, one for each eye.
+ if (scene instanceof Array) {
+ this.renderer.render(scene[0], camera);
+ } else {
+ this.renderer.render(scene, camera);
+ }
+ }
+};
+
+
+WebVRManager.prototype.setMode_ = function(mode) {
+ var oldMode = this.mode;
+ if (mode == this.mode) {
+ console.error('Not changing modes, already in %s', mode);
+ return;
+ }
+ console.log('Mode change: %s => %s', this.mode, mode);
+ this.mode = mode;
+ this.button.setMode(mode, this.isVRCompatible);
+
+ if (this.mode == Modes.VR && Util.isLandscapeMode() && Util.isMobile()) {
+ // In landscape mode, temporarily show the "put into Cardboard"
+ // interstitial. Otherwise, do the default thing.
+ this.rotateInstructions.showTemporarily(3000);
+ } else {
+ this.updateRotateInstructions_();
+ }
+
+ // Also hide the viewer selector.
+ this.viewerSelector.hide();
+
+ // Emit an event indicating the mode changed.
+ this.emit('modechange', mode, oldMode);
+
+ // Note: This is a nasty hack since we need to communicate to the polyfill
+ // that touch panning is disabled, and the only way to do this currently is
+ // via WebVRConfig.
+ // TODO: Maybe move touch panning to the boilerplate to eliminate the hack.
+ //
+ // If we are in VR mode, always disable touch panning.
+ if (this.isTouchPannerEnabled) {
+ if (this.mode == Modes.VR) {
+ WebVRConfig.TOUCH_PANNER_DISABLED = true;
+ } else {
+ WebVRConfig.TOUCH_PANNER_DISABLED = false;
+ }
+ }
+
+ if (this.mode == Modes.VR) {
+ // In VR mode, set the HMDVRDevice parameters.
+ this.setHMDVRDeviceParams_(this.getViewer());
+ }
+};
+
+/**
+ * Main button was clicked.
+ */
+WebVRManager.prototype.onFSClick_ = function() {
+ switch (this.mode) {
+ case Modes.NORMAL:
+ // TODO: Remove this hack when iOS has fullscreen mode.
+ // If this is an iframe on iOS, break out and open in no_fullscreen mode.
+ if (Util.isIOS() && Util.isIFrame()) {
+ var url = window.location.href;
+ url = Util.appendQueryParameter(url, 'no_fullscreen', 'true');
+ url = Util.appendQueryParameter(url, 'start_mode', Modes.MAGIC_WINDOW);
+ top.location.href = url;
+ return;
+ }
+ this.normalToMagicWindow_();
+ this.setMode_(Modes.MAGIC_WINDOW);
+ break;
+ case Modes.MAGIC_WINDOW:
+ if (this.isFullscreenDisabled) {
+ window.history.back();
+ } else {
+ this.anyModeToNormal_();
+ this.setMode_(Modes.NORMAL);
+ }
+ break;
+ }
+};
+
+/**
+ * The VR button was clicked.
+ */
+WebVRManager.prototype.onVRClick_ = function() {
+ // TODO: Remove this hack when iOS has fullscreen mode.
+ // If this is an iframe on iOS, break out and open in no_fullscreen mode.
+ if (this.mode == Modes.NORMAL && Util.isIOS() && Util.isIFrame()) {
+ var url = window.location.href;
+ url = Util.appendQueryParameter(url, 'no_fullscreen', 'true');
+ url = Util.appendQueryParameter(url, 'start_mode', Modes.VR);
+ top.location.href = url;
+ return;
+ }
+ this.anyModeToVR_();
+ this.setMode_(Modes.VR);
+};
+
+/**
+ * Back button was clicked.
+ */
+WebVRManager.prototype.onBackClick_ = function() {
+ if (this.isFullscreenDisabled) {
+ window.history.back();
+ } else {
+ this.anyModeToNormal_();
+ this.setMode_(Modes.NORMAL);
+ }
+};
+
+WebVRManager.prototype.onSettingsClick_ = function() {
+ // Show the viewer selection dialog.
+ this.viewerSelector.show();
+};
+
+/**
+ *
+ * Methods to go between modes.
+ *
+ */
+WebVRManager.prototype.normalToMagicWindow_ = function() {
+ // TODO: Re-enable pointer lock after debugging.
+ //this.requestPointerLock_();
+ this.requestFullscreen_();
+ this.wakelock.request();
+};
+
+WebVRManager.prototype.anyModeToVR_ = function() {
+ // Don't do orientation locking for consistency.
+ //this.requestOrientationLock_();
+ this.requestFullscreen_();
+ //this.effect.setFullScreen(true);
+ this.wakelock.request();
+ this.distorter.patch();
+};
+
+WebVRManager.prototype.vrToMagicWindow_ = function() {
+ //this.releaseOrientationLock_();
+ this.distorter.unpatch();
+
+ // Android bug: when returning from VR, resize the effect.
+ this.resize_();
+}
+
+WebVRManager.prototype.anyModeToNormal_ = function() {
+ //this.effect.setFullScreen(false);
+ this.exitFullscreen_();
+ //this.releaseOrientationLock_();
+ this.releasePointerLock_();
+ this.wakelock.release();
+ this.distorter.unpatch();
+
+ // Android bug: when returning from VR, resize the effect.
+ this.resize_();
+};
+
+WebVRManager.prototype.resizeIfNeeded_ = function(camera) {
+ // Only resize the canvas if it needs to be resized.
+ var size = this.renderer.getSize();
+ if (size.width != window.innerWidth || size.height != window.innerHeight) {
+ this.resize_();
+ }
+};
+
+WebVRManager.prototype.resize_ = function() {
+ this.effect.setSize(window.innerWidth, window.innerHeight);
+ if (this.camera) {
+ this.camera.aspect = window.innerWidth / window.innerHeight;
+ this.camera.updateProjectionMatrix();
+ }
+};
+
+WebVRManager.prototype.onOrientationChange_ = function(e) {
+ this.updateRotateInstructions_();
+ // Also hide the viewer selector.
+ this.viewerSelector.hide();
+};
+
+WebVRManager.prototype.updateRotateInstructions_ = function() {
+ this.rotateInstructions.disableShowTemporarily();
+ // In portrait VR mode, tell the user to rotate to landscape.
+ if (this.mode == Modes.VR && !Util.isLandscapeMode() && Util.isMobile()) {
+ this.rotateInstructions.show();
+ } else {
+ this.rotateInstructions.hide();
+ }
+};
+
+WebVRManager.prototype.onFullscreenChange_ = function(e) {
+ // If we leave full-screen, go back to normal mode.
+ if (document.webkitFullscreenElement === null ||
+ document.mozFullScreenElement === null) {
+ this.anyModeToNormal_();
+ this.setMode_(Modes.NORMAL);
+ }
+};
+
+WebVRManager.prototype.requestPointerLock_ = function() {
+ var canvas = this.renderer.domElement;
+ canvas.requestPointerLock = canvas.requestPointerLock ||
+ canvas.mozRequestPointerLock ||
+ canvas.webkitRequestPointerLock;
+
+ if (canvas.requestPointerLock) {
+ canvas.requestPointerLock();
+ }
+};
+
+WebVRManager.prototype.releasePointerLock_ = function() {
+ document.exitPointerLock = document.exitPointerLock ||
+ document.mozExitPointerLock ||
+ document.webkitExitPointerLock;
+
+ if (document.exitPointerLock) {
+ document.exitPointerLock();
+ }
+};
+
+WebVRManager.prototype.requestOrientationLock_ = function() {
+ if (screen.orientation && Util.isMobile()) {
+ screen.orientation.lock('landscape');
+ }
+};
+
+WebVRManager.prototype.releaseOrientationLock_ = function() {
+ if (screen.orientation) {
+ screen.orientation.unlock();
+ }
+};
+
+WebVRManager.prototype.requestFullscreen_ = function() {
+ var canvas = document.body;
+ //var canvas = this.renderer.domElement;
+ if (canvas.requestFullscreen) {
+ canvas.requestFullscreen();
+ } else if (canvas.mozRequestFullScreen) {
+ canvas.mozRequestFullScreen({vrDisplay: this.hmd});
+ } else if (canvas.webkitRequestFullscreen) {
+ canvas.webkitRequestFullscreen({vrDisplay: this.hmd});
+ } else if (canvas.msRequestFullscreen) {
+ canvas.msRequestFullscreen({vrDisplay: this.hmd});
+ }
+};
+
+WebVRManager.prototype.exitFullscreen_ = function() {
+ if (document.exitFullscreen) {
+ document.exitFullscreen();
+ } else if (document.mozCancelFullScreen) {
+ document.mozCancelFullScreen();
+ } else if (document.webkitExitFullscreen) {
+ document.webkitExitFullscreen();
+ } else if (document.msExitFullscreen) {
+ document.msExitFullscreen();
+ }
+};
+
+WebVRManager.prototype.onViewerChanged_ = function(viewer) {
+ this.deviceInfo.setViewer(viewer);
+
+ // Update the distortion appropriately.
+ this.distorter.updateDeviceInfo(this.deviceInfo);
+
+ // And update the HMDVRDevice parameters.
+ this.setHMDVRDeviceParams_(viewer);
+
+ // Notify anyone interested in this event.
+ this.emit('viewerchange', viewer);
+};
+
+/**
+ * Sets parameters on CardboardHMDVRDevice. These changes are ultimately handled
+ * by VREffect.
+ */
+WebVRManager.prototype.setHMDVRDeviceParams_ = function(viewer) {
+ this.getDeviceByType_(HMDVRDevice).then(function(hmd) {
+ if (!hmd) {
+ return;
+ }
+
+ // If we can set fields of view, do that now.
+ if (hmd.setFieldOfView) {
+ // Calculate the optimal field of view for each eye.
+ hmd.setFieldOfView(this.deviceInfo.getFieldOfViewLeftEye(this.isUndistorted),
+ this.deviceInfo.getFieldOfViewRightEye(this.isUndistorted));
+ }
+
+ // Note: setInterpupillaryDistance is not part of the WebVR standard.
+ if (hmd.setInterpupillaryDistance) {
+ hmd.setInterpupillaryDistance(viewer.interLensDistance);
+ }
+ }.bind(this));
+};
+
+WebVRManager.prototype.onDeviceParamsUpdated_ = function(newParams) {
+ console.log('DPDB reported that device params were updated.');
+ this.deviceInfo.updateDeviceParams(newParams);
+ this.distorter.updateDeviceInfo(this.deviceInfo);
+};
+
+module.exports = WebVRManager;
+
+},{"./button-manager.js":2,"./cardboard-distorter.js":3,"./device-info.js":4,"./dpdb.js":8,"./emitter.js":9,"./modes.js":11,"./rotate-instructions.js":12,"./util.js":13,"./viewer-selector.js":14,"./wakelock.js":15}]},{},[10]);
+
+},{}],7:[function(_dereq_,module,exports){
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof _dereq_=="function"&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof _dereq_=="function"&&_dereq_;for(var o=0;o Util.MAX_TIMESTEP) {
+ console.warn('Invalid timestamps detected. Time step between successive ' +
+ 'gyroscope sensor samples is very small or not monotonic');
+ this.previousTimestampS = timestampS;
+ return;
+ }
+ this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z);
+ this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma);
+
+ // With iOS and Firefox Android, rotationRate is reported in degrees,
+ // so we first convert to radians.
+ if (this.isIOS || this.isFirefoxAndroid) {
+ this.gyroscope.multiplyScalar(Math.PI / 180);
+ }
+
+ this.filter.addAccelMeasurement(this.accelerometer, timestampS);
+ this.filter.addGyroMeasurement(this.gyroscope, timestampS);
+
+ this.previousTimestampS = timestampS;
+};
+
+FusionPositionSensorVRDevice.prototype.onScreenOrientationChange_ =
+ function(screenOrientation) {
+ this.setScreenTransform_();
+};
+
+FusionPositionSensorVRDevice.prototype.setScreenTransform_ = function() {
+ this.worldToScreenQ.set(0, 0, 0, 1);
+ switch (window.orientation) {
+ case 0:
+ break;
+ case 90:
+ this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0, 0, 1), -Math.PI/2);
+ break;
+ case -90:
+ this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI/2);
+ break;
+ case 180:
+ // TODO.
+ break;
+ }
+};
+
+
+module.exports = FusionPositionSensorVRDevice;
+
+},{"./base.js":1,"./complementary-filter.js":3,"./pose-predictor.js":7,"./three-math.js":9,"./touch-panner.js":10,"./util.js":11}],5:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var WebVRPolyfill = _dereq_('./webvr-polyfill.js');
+
+// Initialize a WebVRConfig just in case.
+window.WebVRConfig = window.WebVRConfig || {};
+new WebVRPolyfill();
+
+},{"./webvr-polyfill.js":12}],6:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var PositionSensorVRDevice = _dereq_('./base.js').PositionSensorVRDevice;
+var THREE = _dereq_('./three-math.js');
+var Util = _dereq_('./util.js');
+
+// How much to rotate per key stroke.
+var KEY_SPEED = 0.15;
+var KEY_ANIMATION_DURATION = 80;
+
+// How much to rotate for mouse events.
+var MOUSE_SPEED_X = 0.5;
+var MOUSE_SPEED_Y = 0.3;
+
+/**
+ * A virtual position sensor, implemented using keyboard and
+ * mouse APIs. This is designed as for desktops/laptops where no Device*
+ * events work.
+ */
+function MouseKeyboardPositionSensorVRDevice() {
+ this.deviceId = 'webvr-polyfill:mouse-keyboard';
+ this.deviceName = 'VR Position Device (webvr-polyfill:mouse-keyboard)';
+
+ // Attach to mouse and keyboard events.
+ window.addEventListener('keydown', this.onKeyDown_.bind(this));
+ window.addEventListener('mousemove', this.onMouseMove_.bind(this));
+ window.addEventListener('mousedown', this.onMouseDown_.bind(this));
+ window.addEventListener('mouseup', this.onMouseUp_.bind(this));
+
+ this.phi = 0;
+ this.theta = 0;
+
+ // Variables for keyboard-based rotation animation.
+ this.targetAngle = null;
+
+ // State variables for calculations.
+ this.euler = new THREE.Euler();
+ this.orientation = new THREE.Quaternion();
+
+ // Variables for mouse-based rotation.
+ this.rotateStart = new THREE.Vector2();
+ this.rotateEnd = new THREE.Vector2();
+ this.rotateDelta = new THREE.Vector2();
+}
+MouseKeyboardPositionSensorVRDevice.prototype = new PositionSensorVRDevice();
+
+/**
+ * Returns {orientation: {x,y,z,w}, position: null}.
+ * Position is not supported for parity with other PositionSensors.
+ */
+MouseKeyboardPositionSensorVRDevice.prototype.getState = function() {
+ this.euler.set(this.phi, this.theta, 0, 'YXZ');
+ this.orientation.setFromEuler(this.euler);
+
+ return {
+ hasOrientation: true,
+ orientation: this.orientation,
+ hasPosition: false,
+ position: null
+ }
+};
+
+MouseKeyboardPositionSensorVRDevice.prototype.onKeyDown_ = function(e) {
+ // Track WASD and arrow keys.
+ if (e.keyCode == 38) { // Up key.
+ this.animatePhi_(this.phi + KEY_SPEED);
+ } else if (e.keyCode == 39) { // Right key.
+ this.animateTheta_(this.theta - KEY_SPEED);
+ } else if (e.keyCode == 40) { // Down key.
+ this.animatePhi_(this.phi - KEY_SPEED);
+ } else if (e.keyCode == 37) { // Left key.
+ this.animateTheta_(this.theta + KEY_SPEED);
+ }
+};
+
+MouseKeyboardPositionSensorVRDevice.prototype.animateTheta_ = function(targetAngle) {
+ this.animateKeyTransitions_('theta', targetAngle);
+};
+
+MouseKeyboardPositionSensorVRDevice.prototype.animatePhi_ = function(targetAngle) {
+ // Prevent looking too far up or down.
+ targetAngle = Util.clamp(targetAngle, -Math.PI/2, Math.PI/2);
+ this.animateKeyTransitions_('phi', targetAngle);
+};
+
+/**
+ * Start an animation to transition an angle from one value to another.
+ */
+MouseKeyboardPositionSensorVRDevice.prototype.animateKeyTransitions_ = function(angleName, targetAngle) {
+ // If an animation is currently running, cancel it.
+ if (this.angleAnimation) {
+ clearInterval(this.angleAnimation);
+ }
+ var startAngle = this[angleName];
+ var startTime = new Date();
+ // Set up an interval timer to perform the animation.
+ this.angleAnimation = setInterval(function() {
+ // Once we're finished the animation, we're done.
+ var elapsed = new Date() - startTime;
+ if (elapsed >= KEY_ANIMATION_DURATION) {
+ this[angleName] = targetAngle;
+ clearInterval(this.angleAnimation);
+ return;
+ }
+ // Linearly interpolate the angle some amount.
+ var percent = elapsed / KEY_ANIMATION_DURATION;
+ this[angleName] = startAngle + (targetAngle - startAngle) * percent;
+ }.bind(this), 1000/60);
+};
+
+MouseKeyboardPositionSensorVRDevice.prototype.onMouseDown_ = function(e) {
+ this.rotateStart.set(e.clientX, e.clientY);
+ this.isDragging = true;
+};
+
+// Very similar to https://gist.github.com/mrflix/8351020
+MouseKeyboardPositionSensorVRDevice.prototype.onMouseMove_ = function(e) {
+ if (!this.isDragging && !this.isPointerLocked_()) {
+ return;
+ }
+ // Support pointer lock API.
+ if (this.isPointerLocked_()) {
+ var movementX = e.movementX || e.mozMovementX || 0;
+ var movementY = e.movementY || e.mozMovementY || 0;
+ this.rotateEnd.set(this.rotateStart.x - movementX, this.rotateStart.y - movementY);
+ } else {
+ this.rotateEnd.set(e.clientX, e.clientY);
+ }
+ // Calculate how much we moved in mouse space.
+ this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart);
+ this.rotateStart.copy(this.rotateEnd);
+
+ // Keep track of the cumulative euler angles.
+ var element = document.body;
+ this.phi += 2 * Math.PI * this.rotateDelta.y / element.clientHeight * MOUSE_SPEED_Y;
+ this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * MOUSE_SPEED_X;
+
+ // Prevent looking too far up or down.
+ this.phi = Util.clamp(this.phi, -Math.PI/2, Math.PI/2);
+};
+
+MouseKeyboardPositionSensorVRDevice.prototype.onMouseUp_ = function(e) {
+ this.isDragging = false;
+};
+
+MouseKeyboardPositionSensorVRDevice.prototype.isPointerLocked_ = function() {
+ var el = document.pointerLockElement || document.mozPointerLockElement ||
+ document.webkitPointerLockElement;
+ return el !== undefined;
+};
+
+MouseKeyboardPositionSensorVRDevice.prototype.resetSensor = function() {
+ console.error('Not implemented yet.');
+};
+
+module.exports = MouseKeyboardPositionSensorVRDevice;
+
+},{"./base.js":1,"./three-math.js":9,"./util.js":11}],7:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var THREE = _dereq_('./three-math.js');
+
+var DEBUG = false;
+
+/**
+ * Given an orientation and the gyroscope data, predicts the future orientation
+ * of the head. This makes rendering appear faster.
+ *
+ * Also see: http://msl.cs.uiuc.edu/~lavalle/papers/LavYerKatAnt14.pdf
+ *
+ * @param {Number} predictionTimeS time from head movement to the appearance of
+ * the corresponding image.
+ */
+function PosePredictor(predictionTimeS) {
+ this.predictionTimeS = predictionTimeS;
+
+ // The quaternion corresponding to the previous state.
+ this.previousQ = new THREE.Quaternion();
+ // Previous time a prediction occurred.
+ this.previousTimestampS = null;
+
+ // The delta quaternion that adjusts the current pose.
+ this.deltaQ = new THREE.Quaternion();
+ // The output quaternion.
+ this.outQ = new THREE.Quaternion();
+}
+
+PosePredictor.prototype.getPrediction = function(currentQ, gyro, timestampS) {
+ if (!this.previousTimestampS) {
+ this.previousQ.copy(currentQ);
+ this.previousTimestampS = timestampS;
+ return currentQ;
+ }
+
+ // Calculate axis and angle based on gyroscope rotation rate data.
+ var axis = new THREE.Vector3();
+ axis.copy(gyro);
+ axis.normalize();
+
+ var angularSpeed = gyro.length();
+
+ // If we're rotating slowly, don't do prediction.
+ if (angularSpeed < THREE.Math.degToRad(20)) {
+ if (DEBUG) {
+ console.log('Moving slowly, at %s deg/s: no prediction',
+ THREE.Math.radToDeg(angularSpeed).toFixed(1));
+ }
+ this.outQ.copy(currentQ);
+ this.previousQ.copy(currentQ);
+ return this.outQ;
+ }
+
+ // Get the predicted angle based on the time delta and latency.
+ var deltaT = timestampS - this.previousTimestampS;
+ var predictAngle = angularSpeed * this.predictionTimeS;
+
+ this.deltaQ.setFromAxisAngle(axis, predictAngle);
+ this.outQ.copy(this.previousQ);
+ this.outQ.multiply(this.deltaQ);
+
+ this.previousQ.copy(currentQ);
+
+ return this.outQ;
+};
+
+
+module.exports = PosePredictor;
+
+},{"./three-math.js":9}],8:[function(_dereq_,module,exports){
+function SensorSample(sample, timestampS) {
+ this.set(sample, timestampS);
+};
+
+SensorSample.prototype.set = function(sample, timestampS) {
+ this.sample = sample;
+ this.timestampS = timestampS;
+};
+
+SensorSample.prototype.copy = function(sensorSample) {
+ this.set(sensorSample.sample, sensorSample.timestampS);
+};
+
+module.exports = SensorSample;
+
+},{}],9:[function(_dereq_,module,exports){
+/*
+ * A subset of THREE.js, providing mostly quaternion and euler-related
+ * operations, manually lifted from
+ * https://github.com/mrdoob/three.js/tree/master/src/math, as of 9c30286b38df039fca389989ff06ea1c15d6bad1
+ */
+
+// Only use if the real THREE is not provided.
+var THREE = window.THREE || {};
+
+// If some piece of THREE is missing, fill it in here.
+if (!THREE.Quaternion || !THREE.Vector3 || !THREE.Vector2 || !THREE.Euler || !THREE.Math) {
+console.log('No THREE.js found.');
+
+
+/*** START Quaternion ***/
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://exocortex.com
+ */
+
+THREE.Quaternion = function ( x, y, z, w ) {
+
+ this._x = x || 0;
+ this._y = y || 0;
+ this._z = z || 0;
+ this._w = ( w !== undefined ) ? w : 1;
+
+};
+
+THREE.Quaternion.prototype = {
+
+ constructor: THREE.Quaternion,
+
+ _x: 0,_y: 0, _z: 0, _w: 0,
+
+ get x () {
+
+ return this._x;
+
+ },
+
+ set x ( value ) {
+
+ this._x = value;
+ this.onChangeCallback();
+
+ },
+
+ get y () {
+
+ return this._y;
+
+ },
+
+ set y ( value ) {
+
+ this._y = value;
+ this.onChangeCallback();
+
+ },
+
+ get z () {
+
+ return this._z;
+
+ },
+
+ set z ( value ) {
+
+ this._z = value;
+ this.onChangeCallback();
+
+ },
+
+ get w () {
+
+ return this._w;
+
+ },
+
+ set w ( value ) {
+
+ this._w = value;
+ this.onChangeCallback();
+
+ },
+
+ set: function ( x, y, z, w ) {
+
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._w = w;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ copy: function ( quaternion ) {
+
+ this._x = quaternion.x;
+ this._y = quaternion.y;
+ this._z = quaternion.z;
+ this._w = quaternion.w;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromEuler: function ( euler, update ) {
+
+ if ( euler instanceof THREE.Euler === false ) {
+
+ throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
+ }
+
+ // http://www.mathworks.com/matlabcentral/fileexchange/
+ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
+ // content/SpinCalc.m
+
+ var c1 = Math.cos( euler._x / 2 );
+ var c2 = Math.cos( euler._y / 2 );
+ var c3 = Math.cos( euler._z / 2 );
+ var s1 = Math.sin( euler._x / 2 );
+ var s2 = Math.sin( euler._y / 2 );
+ var s3 = Math.sin( euler._z / 2 );
+
+ if ( euler.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 ( euler.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 ( euler.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 ( euler.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 ( euler.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 ( euler.order === 'XZY' ) {
+
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+
+ }
+
+ if ( update !== false ) this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromAxisAngle: function ( axis, angle ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
+
+ // assumes axis is normalized
+
+ var halfAngle = angle / 2, s = Math.sin( halfAngle );
+
+ this._x = axis.x * s;
+ this._y = axis.y * s;
+ this._z = axis.z * s;
+ this._w = Math.cos( halfAngle );
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromRotationMatrix: function ( m ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ var te = m.elements,
+
+ m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+ m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+ m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
+
+ trace = m11 + m22 + m33,
+ s;
+
+ if ( trace > 0 ) {
+
+ s = 0.5 / Math.sqrt( trace + 1.0 );
+
+ this._w = 0.25 / s;
+ this._x = ( m32 - m23 ) * s;
+ this._y = ( m13 - m31 ) * s;
+ this._z = ( m21 - m12 ) * s;
+
+ } else if ( m11 > m22 && m11 > m33 ) {
+
+ s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
+
+ this._w = ( m32 - m23 ) / s;
+ this._x = 0.25 * s;
+ this._y = ( m12 + m21 ) / s;
+ this._z = ( m13 + m31 ) / s;
+
+ } else if ( m22 > m33 ) {
+
+ s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
+
+ this._w = ( m13 - m31 ) / s;
+ this._x = ( m12 + m21 ) / s;
+ this._y = 0.25 * s;
+ this._z = ( m23 + m32 ) / s;
+
+ } else {
+
+ s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
+
+ this._w = ( m21 - m12 ) / s;
+ this._x = ( m13 + m31 ) / s;
+ this._y = ( m23 + m32 ) / s;
+ this._z = 0.25 * s;
+
+ }
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromUnitVectors: function () {
+
+ // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
+
+ // assumes direction vectors vFrom and vTo are normalized
+
+ var v1, r;
+
+ var EPS = 0.000001;
+
+ return function ( vFrom, vTo ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ r = vFrom.dot( vTo ) + 1;
+
+ if ( r < EPS ) {
+
+ r = 0;
+
+ if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
+
+ v1.set( - vFrom.y, vFrom.x, 0 );
+
+ } else {
+
+ v1.set( 0, - vFrom.z, vFrom.y );
+
+ }
+
+ } else {
+
+ v1.crossVectors( vFrom, vTo );
+
+ }
+
+ this._x = v1.x;
+ this._y = v1.y;
+ this._z = v1.z;
+ this._w = r;
+
+ this.normalize();
+
+ return this;
+
+ }
+
+ }(),
+
+ inverse: function () {
+
+ this.conjugate().normalize();
+
+ return this;
+
+ },
+
+ conjugate: function () {
+
+ this._x *= - 1;
+ this._y *= - 1;
+ this._z *= - 1;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
+
+ },
+
+ lengthSq: function () {
+
+ return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
+
+ },
+
+ normalize: function () {
+
+ var l = this.length();
+
+ if ( l === 0 ) {
+
+ this._x = 0;
+ this._y = 0;
+ this._z = 0;
+ this._w = 1;
+
+ } else {
+
+ l = 1 / l;
+
+ this._x = this._x * l;
+ this._y = this._y * l;
+ this._z = this._z * l;
+ this._w = this._w * l;
+
+ }
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ multiply: function ( q, p ) {
+
+ if ( p !== undefined ) {
+
+ console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
+ return this.multiplyQuaternions( q, p );
+
+ }
+
+ return this.multiplyQuaternions( this, q );
+
+ },
+
+ multiplyQuaternions: function ( a, b ) {
+
+ // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
+
+ var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
+ var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
+
+ this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
+ this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
+ this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
+ this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ multiplyVector3: function ( vector ) {
+
+ console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );
+ return vector.applyQuaternion( this );
+
+ },
+
+ slerp: function ( qb, t ) {
+
+ if ( t === 0 ) return this;
+ if ( t === 1 ) return this.copy( qb );
+
+ var x = this._x, y = this._y, z = this._z, w = this._w;
+
+ // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
+
+ var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
+
+ if ( cosHalfTheta < 0 ) {
+
+ this._w = - qb._w;
+ this._x = - qb._x;
+ this._y = - qb._y;
+ this._z = - qb._z;
+
+ cosHalfTheta = - cosHalfTheta;
+
+ } else {
+
+ this.copy( qb );
+
+ }
+
+ if ( cosHalfTheta >= 1.0 ) {
+
+ this._w = w;
+ this._x = x;
+ this._y = y;
+ this._z = z;
+
+ return this;
+
+ }
+
+ var halfTheta = Math.acos( cosHalfTheta );
+ var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
+
+ if ( Math.abs( sinHalfTheta ) < 0.001 ) {
+
+ this._w = 0.5 * ( w + this._w );
+ this._x = 0.5 * ( x + this._x );
+ this._y = 0.5 * ( y + this._y );
+ this._z = 0.5 * ( z + this._z );
+
+ return this;
+
+ }
+
+ var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
+ ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
+
+ this._w = ( w * ratioA + this._w * ratioB );
+ this._x = ( x * ratioA + this._x * ratioB );
+ this._y = ( y * ratioA + this._y * ratioB );
+ this._z = ( z * ratioA + this._z * ratioB );
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ equals: function ( quaternion ) {
+
+ return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this._x = array[ offset ];
+ this._y = array[ offset + 1 ];
+ this._z = array[ offset + 2 ];
+ this._w = array[ offset + 3 ];
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this._x;
+ array[ offset + 1 ] = this._y;
+ array[ offset + 2 ] = this._z;
+ array[ offset + 3 ] = this._w;
+
+ return array;
+
+ },
+
+ onChange: function ( callback ) {
+
+ this.onChangeCallback = callback;
+
+ return this;
+
+ },
+
+ onChangeCallback: function () {},
+
+ clone: function () {
+
+ return new THREE.Quaternion( this._x, this._y, this._z, this._w );
+
+ }
+
+};
+
+THREE.Quaternion.slerp = function ( qa, qb, qm, t ) {
+
+ return qm.copy( qa ).slerp( qb, t );
+
+}
+
+/*** END Quaternion ***/
+/*** START Vector2 ***/
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author philogb / http://blog.thejit.org/
+ * @author egraether / http://egraether.com/
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ */
+
+THREE.Vector2 = function ( x, y ) {
+
+ this.x = x || 0;
+ this.y = y || 0;
+
+};
+
+THREE.Vector2.prototype = {
+
+ constructor: THREE.Vector2,
+
+ set: function ( x, y ) {
+
+ this.x = x;
+ this.y = y;
+
+ return this;
+
+ },
+
+ setX: function ( x ) {
+
+ this.x = x;
+
+ return this;
+
+ },
+
+ setY: function ( y ) {
+
+ this.y = y;
+
+ return this;
+
+ },
+
+ setComponent: function ( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ getComponent: function ( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+
+ return this;
+
+ },
+
+ multiply: function ( v ) {
+
+ this.x *= v.x;
+ this.y *= v.y;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( s ) {
+
+ this.x *= s;
+ this.y *= s;
+
+ return this;
+
+ },
+
+ divide: function ( v ) {
+
+ this.x /= v.x;
+ this.y /= v.y;
+
+ return this;
+
+ },
+
+ divideScalar: function ( scalar ) {
+
+ if ( scalar !== 0 ) {
+
+ var invScalar = 1 / scalar;
+
+ this.x *= invScalar;
+ this.y *= invScalar;
+
+ } else {
+
+ this.x = 0;
+ this.y = 0;
+
+ }
+
+ return this;
+
+ },
+
+ min: function ( v ) {
+
+ if ( this.x > v.x ) {
+
+ this.x = v.x;
+
+ }
+
+ if ( this.y > v.y ) {
+
+ this.y = v.y;
+
+ }
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ if ( this.x < v.x ) {
+
+ this.x = v.x;
+
+ }
+
+ if ( this.y < v.y ) {
+
+ this.y = v.y;
+
+ }
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // This function assumes min < max, if this assumption isn't true it will not operate correctly
+
+ if ( this.x < min.x ) {
+
+ this.x = min.x;
+
+ } else if ( this.x > max.x ) {
+
+ this.x = max.x;
+
+ }
+
+ if ( this.y < min.y ) {
+
+ this.y = min.y;
+
+ } else if ( this.y > max.y ) {
+
+ this.y = max.y;
+
+ }
+
+ return this;
+ },
+
+ clampScalar: ( function () {
+
+ var min, max;
+
+ return function ( minVal, maxVal ) {
+
+ if ( min === undefined ) {
+
+ min = new THREE.Vector2();
+ max = new THREE.Vector2();
+
+ }
+
+ min.set( minVal, minVal );
+ max.set( maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ } )(),
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y;
+
+ },
+
+ lengthSq: function () {
+
+ return this.x * this.x + this.y * this.y;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this.x * this.x + this.y * this.y );
+
+ },
+
+ normalize: function () {
+
+ return this.divideScalar( this.length() );
+
+ },
+
+ distanceTo: function ( v ) {
+
+ return Math.sqrt( this.distanceToSquared( v ) );
+
+ },
+
+ distanceToSquared: function ( v ) {
+
+ var dx = this.x - v.x, dy = this.y - v.y;
+ return dx * dx + dy * dy;
+
+ },
+
+ setLength: function ( l ) {
+
+ var oldLength = this.length();
+
+ if ( oldLength !== 0 && l !== oldLength ) {
+
+ this.multiplyScalar( l / oldLength );
+ }
+
+ return this;
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+
+ return this;
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+
+ return array;
+
+ },
+
+ fromAttribute: function ( attribute, index, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ index = index * attribute.itemSize + offset;
+
+ this.x = attribute.array[ index ];
+ this.y = attribute.array[ index + 1 ];
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new THREE.Vector2( this.x, this.y );
+
+ }
+
+};
+/*** END Vector2 ***/
+/*** START Vector3 ***/
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author *kile / http://kile.stravaganza.org/
+ * @author philogb / http://blog.thejit.org/
+ * @author mikael emtinger / http://gomo.se/
+ * @author egraether / http://egraether.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.Vector3 = function ( x, y, z ) {
+
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+
+};
+
+THREE.Vector3.prototype = {
+
+ constructor: THREE.Vector3,
+
+ set: function ( x, y, z ) {
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+
+ return this;
+
+ },
+
+ setX: function ( x ) {
+
+ this.x = x;
+
+ return this;
+
+ },
+
+ setY: function ( y ) {
+
+ this.y = y;
+
+ return this;
+
+ },
+
+ setZ: function ( z ) {
+
+ this.z = z;
+
+ return this;
+
+ },
+
+ setComponent: function ( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ case 2: this.z = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ getComponent: function ( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ case 2: return this.z;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+ this.z += v.z;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+ this.z += s;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+ this.z = a.z + b.z;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+ this.z -= v.z;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+ this.z = a.z - b.z;
+
+ return this;
+
+ },
+
+ multiply: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
+ return this.multiplyVectors( v, w );
+
+ }
+
+ this.x *= v.x;
+ this.y *= v.y;
+ this.z *= v.z;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( scalar ) {
+
+ this.x *= scalar;
+ this.y *= scalar;
+ this.z *= scalar;
+
+ return this;
+
+ },
+
+ multiplyVectors: function ( a, b ) {
+
+ this.x = a.x * b.x;
+ this.y = a.y * b.y;
+ this.z = a.z * b.z;
+
+ return this;
+
+ },
+
+ applyEuler: function () {
+
+ var quaternion;
+
+ return function ( euler ) {
+
+ if ( euler instanceof THREE.Euler === false ) {
+
+ console.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
+
+ this.applyQuaternion( quaternion.setFromEuler( euler ) );
+
+ return this;
+
+ };
+
+ }(),
+
+ applyAxisAngle: function () {
+
+ var quaternion;
+
+ return function ( axis, angle ) {
+
+ if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
+
+ this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
+
+ return this;
+
+ };
+
+ }(),
+
+ applyMatrix3: function ( m ) {
+
+ var x = this.x;
+ var y = this.y;
+ var z = this.z;
+
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
+ this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
+ this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
+
+ return this;
+
+ },
+
+ applyMatrix4: function ( m ) {
+
+ // input: THREE.Matrix4 affine matrix
+
+ var x = this.x, y = this.y, z = this.z;
+
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ];
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ];
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ];
+
+ return this;
+
+ },
+
+ applyProjection: function ( m ) {
+
+ // input: THREE.Matrix4 projection matrix
+
+ var x = this.x, y = this.y, z = this.z;
+
+ var e = m.elements;
+ var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide
+
+ this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d;
+ this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d;
+ this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d;
+
+ return this;
+
+ },
+
+ applyQuaternion: function ( q ) {
+
+ var x = this.x;
+ var y = this.y;
+ var z = this.z;
+
+ var qx = q.x;
+ var qy = q.y;
+ var qz = q.z;
+ var qw = q.w;
+
+ // calculate quat * vector
+
+ var ix = qw * x + qy * z - qz * y;
+ var iy = qw * y + qz * x - qx * z;
+ var iz = qw * z + qx * y - qy * x;
+ var iw = - qx * x - qy * y - qz * z;
+
+ // calculate result * inverse quat
+
+ this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
+ this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
+ this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
+
+ return this;
+
+ },
+
+ project: function () {
+
+ var matrix;
+
+ return function ( camera ) {
+
+ if ( matrix === undefined ) matrix = new THREE.Matrix4();
+
+ matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
+ return this.applyProjection( matrix );
+
+ };
+
+ }(),
+
+ unproject: function () {
+
+ var matrix;
+
+ return function ( camera ) {
+
+ if ( matrix === undefined ) matrix = new THREE.Matrix4();
+
+ matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
+ return this.applyProjection( matrix );
+
+ };
+
+ }(),
+
+ transformDirection: function ( m ) {
+
+ // input: THREE.Matrix4 affine matrix
+ // vector interpreted as a direction
+
+ var x = this.x, y = this.y, z = this.z;
+
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
+
+ this.normalize();
+
+ return this;
+
+ },
+
+ divide: function ( v ) {
+
+ this.x /= v.x;
+ this.y /= v.y;
+ this.z /= v.z;
+
+ return this;
+
+ },
+
+ divideScalar: function ( scalar ) {
+
+ if ( scalar !== 0 ) {
+
+ var invScalar = 1 / scalar;
+
+ this.x *= invScalar;
+ this.y *= invScalar;
+ this.z *= invScalar;
+
+ } else {
+
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+
+ }
+
+ return this;
+
+ },
+
+ min: function ( v ) {
+
+ if ( this.x > v.x ) {
+
+ this.x = v.x;
+
+ }
+
+ if ( this.y > v.y ) {
+
+ this.y = v.y;
+
+ }
+
+ if ( this.z > v.z ) {
+
+ this.z = v.z;
+
+ }
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ if ( this.x < v.x ) {
+
+ this.x = v.x;
+
+ }
+
+ if ( this.y < v.y ) {
+
+ this.y = v.y;
+
+ }
+
+ if ( this.z < v.z ) {
+
+ this.z = v.z;
+
+ }
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // This function assumes min < max, if this assumption isn't true it will not operate correctly
+
+ if ( this.x < min.x ) {
+
+ this.x = min.x;
+
+ } else if ( this.x > max.x ) {
+
+ this.x = max.x;
+
+ }
+
+ if ( this.y < min.y ) {
+
+ this.y = min.y;
+
+ } else if ( this.y > max.y ) {
+
+ this.y = max.y;
+
+ }
+
+ if ( this.z < min.z ) {
+
+ this.z = min.z;
+
+ } else if ( this.z > max.z ) {
+
+ this.z = max.z;
+
+ }
+
+ return this;
+
+ },
+
+ clampScalar: ( function () {
+
+ var min, max;
+
+ return function ( minVal, maxVal ) {
+
+ if ( min === undefined ) {
+
+ min = new THREE.Vector3();
+ max = new THREE.Vector3();
+
+ }
+
+ min.set( minVal, minVal, minVal );
+ max.set( maxVal, maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ } )(),
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+ this.z = Math.floor( this.z );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+ this.z = Math.ceil( this.z );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+ this.z = Math.round( this.z );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+ this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+ this.z = - this.z;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y + this.z * v.z;
+
+ },
+
+ 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 );
+
+ },
+
+ lengthManhattan: function () {
+
+ return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
+
+ },
+
+ normalize: function () {
+
+ return this.divideScalar( this.length() );
+
+ },
+
+ setLength: function ( l ) {
+
+ var oldLength = this.length();
+
+ if ( oldLength !== 0 && l !== oldLength ) {
+
+ this.multiplyScalar( l / oldLength );
+ }
+
+ return this;
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+ this.z += ( v.z - this.z ) * alpha;
+
+ return this;
+
+ },
+
+ cross: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
+ return this.crossVectors( v, w );
+
+ }
+
+ var x = this.x, y = this.y, z = this.z;
+
+ this.x = y * v.z - z * v.y;
+ this.y = z * v.x - x * v.z;
+ this.z = x * v.y - y * v.x;
+
+ return this;
+
+ },
+
+ crossVectors: function ( a, b ) {
+
+ var ax = a.x, ay = a.y, az = a.z;
+ var bx = b.x, by = b.y, bz = b.z;
+
+ this.x = ay * bz - az * by;
+ this.y = az * bx - ax * bz;
+ this.z = ax * by - ay * bx;
+
+ return this;
+
+ },
+
+ projectOnVector: function () {
+
+ var v1, dot;
+
+ return function ( vector ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ v1.copy( vector ).normalize();
+
+ dot = this.dot( v1 );
+
+ return this.copy( v1 ).multiplyScalar( dot );
+
+ };
+
+ }(),
+
+ projectOnPlane: function () {
+
+ var v1;
+
+ return function ( planeNormal ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ v1.copy( this ).projectOnVector( planeNormal );
+
+ return this.sub( v1 );
+
+ }
+
+ }(),
+
+ reflect: function () {
+
+ // reflect incident vector off plane orthogonal to normal
+ // normal is assumed to have unit length
+
+ var v1;
+
+ return function ( normal ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
+
+ }
+
+ }(),
+
+ angleTo: function ( v ) {
+
+ var theta = this.dot( v ) / ( this.length() * v.length() );
+
+ // clamp, to handle numerical problems
+
+ return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) );
+
+ },
+
+ distanceTo: function ( v ) {
+
+ return Math.sqrt( this.distanceToSquared( v ) );
+
+ },
+
+ distanceToSquared: function ( v ) {
+
+ var dx = this.x - v.x;
+ var dy = this.y - v.y;
+ var dz = this.z - v.z;
+
+ return dx * dx + dy * dy + dz * dz;
+
+ },
+
+ setEulerFromRotationMatrix: function ( m, order ) {
+
+ console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' );
+
+ },
+
+ setEulerFromQuaternion: function ( q, order ) {
+
+ console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' );
+
+ },
+
+ getPositionFromMatrix: function ( m ) {
+
+ console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' );
+
+ return this.setFromMatrixPosition( m );
+
+ },
+
+ getScaleFromMatrix: function ( m ) {
+
+ console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' );
+
+ return this.setFromMatrixScale( m );
+ },
+
+ getColumnFromMatrix: function ( index, matrix ) {
+
+ console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' );
+
+ return this.setFromMatrixColumn( index, matrix );
+
+ },
+
+ setFromMatrixPosition: function ( m ) {
+
+ this.x = m.elements[ 12 ];
+ this.y = m.elements[ 13 ];
+ this.z = m.elements[ 14 ];
+
+ return this;
+
+ },
+
+ setFromMatrixScale: function ( m ) {
+
+ var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length();
+ var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length();
+ var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length();
+
+ this.x = sx;
+ this.y = sy;
+ this.z = sz;
+
+ return this;
+ },
+
+ setFromMatrixColumn: function ( index, matrix ) {
+
+ var offset = index * 4;
+
+ var me = matrix.elements;
+
+ this.x = me[ offset ];
+ this.y = me[ offset + 1 ];
+ this.z = me[ offset + 2 ];
+
+ return this;
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+ this.z = array[ offset + 2 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+ array[ offset + 2 ] = this.z;
+
+ return array;
+
+ },
+
+ fromAttribute: function ( attribute, index, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ index = index * attribute.itemSize + offset;
+
+ this.x = attribute.array[ index ];
+ this.y = attribute.array[ index + 1 ];
+ this.z = attribute.array[ index + 2 ];
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new THREE.Vector3( this.x, this.y, this.z );
+
+ }
+
+};
+/*** END Vector3 ***/
+/*** START Euler ***/
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://exocortex.com
+ */
+
+THREE.Euler = function ( x, y, z, order ) {
+
+ this._x = x || 0;
+ this._y = y || 0;
+ this._z = z || 0;
+ this._order = order || THREE.Euler.DefaultOrder;
+
+};
+
+THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];
+
+THREE.Euler.DefaultOrder = 'XYZ';
+
+THREE.Euler.prototype = {
+
+ constructor: THREE.Euler,
+
+ _x: 0, _y: 0, _z: 0, _order: THREE.Euler.DefaultOrder,
+
+ get x () {
+
+ return this._x;
+
+ },
+
+ set x ( value ) {
+
+ this._x = value;
+ this.onChangeCallback();
+
+ },
+
+ get y () {
+
+ return this._y;
+
+ },
+
+ set y ( value ) {
+
+ this._y = value;
+ this.onChangeCallback();
+
+ },
+
+ get z () {
+
+ return this._z;
+
+ },
+
+ set z ( value ) {
+
+ this._z = value;
+ this.onChangeCallback();
+
+ },
+
+ get order () {
+
+ return this._order;
+
+ },
+
+ set order ( value ) {
+
+ this._order = value;
+ this.onChangeCallback();
+
+ },
+
+ set: function ( x, y, z, order ) {
+
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._order = order || this._order;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ copy: function ( euler ) {
+
+ this._x = euler._x;
+ this._y = euler._y;
+ this._z = euler._z;
+ this._order = euler._order;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromRotationMatrix: function ( m, order, update ) {
+
+ var clamp = THREE.Math.clamp;
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ var te = m.elements;
+ var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
+ var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
+ var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
+
+ order = order || this._order;
+
+ if ( order === 'XYZ' ) {
+
+ this._y = Math.asin( clamp( m13, - 1, 1 ) );
+
+ if ( Math.abs( m13 ) < 0.99999 ) {
+
+ this._x = Math.atan2( - m23, m33 );
+ this._z = Math.atan2( - m12, m11 );
+
+ } else {
+
+ this._x = Math.atan2( m32, m22 );
+ this._z = 0;
+
+ }
+
+ } else if ( order === 'YXZ' ) {
+
+ this._x = Math.asin( - clamp( m23, - 1, 1 ) );
+
+ if ( Math.abs( m23 ) < 0.99999 ) {
+
+ this._y = Math.atan2( m13, m33 );
+ this._z = Math.atan2( m21, m22 );
+
+ } else {
+
+ this._y = Math.atan2( - m31, m11 );
+ this._z = 0;
+
+ }
+
+ } else if ( order === 'ZXY' ) {
+
+ this._x = Math.asin( clamp( m32, - 1, 1 ) );
+
+ if ( Math.abs( m32 ) < 0.99999 ) {
+
+ this._y = Math.atan2( - m31, m33 );
+ this._z = Math.atan2( - m12, m22 );
+
+ } else {
+
+ this._y = 0;
+ this._z = Math.atan2( m21, m11 );
+
+ }
+
+ } else if ( order === 'ZYX' ) {
+
+ this._y = Math.asin( - clamp( m31, - 1, 1 ) );
+
+ if ( Math.abs( m31 ) < 0.99999 ) {
+
+ this._x = Math.atan2( m32, m33 );
+ this._z = Math.atan2( m21, m11 );
+
+ } else {
+
+ this._x = 0;
+ this._z = Math.atan2( - m12, m22 );
+
+ }
+
+ } else if ( order === 'YZX' ) {
+
+ this._z = Math.asin( clamp( m21, - 1, 1 ) );
+
+ if ( Math.abs( m21 ) < 0.99999 ) {
+
+ this._x = Math.atan2( - m23, m22 );
+ this._y = Math.atan2( - m31, m11 );
+
+ } else {
+
+ this._x = 0;
+ this._y = Math.atan2( m13, m33 );
+
+ }
+
+ } else if ( order === 'XZY' ) {
+
+ this._z = Math.asin( - clamp( m12, - 1, 1 ) );
+
+ if ( Math.abs( m12 ) < 0.99999 ) {
+
+ this._x = Math.atan2( m32, m22 );
+ this._y = Math.atan2( m13, m11 );
+
+ } else {
+
+ this._x = Math.atan2( - m23, m33 );
+ this._y = 0;
+
+ }
+
+ } else {
+
+ console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order )
+
+ }
+
+ this._order = order;
+
+ if ( update !== false ) this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromQuaternion: function () {
+
+ var matrix;
+
+ return function ( q, order, update ) {
+
+ if ( matrix === undefined ) matrix = new THREE.Matrix4();
+ matrix.makeRotationFromQuaternion( q );
+ this.setFromRotationMatrix( matrix, order, update );
+
+ return this;
+
+ };
+
+ }(),
+
+ setFromVector3: function ( v, order ) {
+
+ return this.set( v.x, v.y, v.z, order || this._order );
+
+ },
+
+ reorder: function () {
+
+ // WARNING: this discards revolution information -bhouston
+
+ var q = new THREE.Quaternion();
+
+ return function ( newOrder ) {
+
+ q.setFromEuler( this );
+ this.setFromQuaternion( q, newOrder );
+
+ };
+
+ }(),
+
+ equals: function ( euler ) {
+
+ return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
+
+ },
+
+ fromArray: function ( array ) {
+
+ this._x = array[ 0 ];
+ this._y = array[ 1 ];
+ this._z = array[ 2 ];
+ if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ toArray: function () {
+
+ return [ this._x, this._y, this._z, this._order ];
+
+ },
+
+ toVector3: function ( optionalResult ) {
+
+ if ( optionalResult ) {
+
+ return optionalResult.set( this._x, this._y, this._z );
+
+ } else {
+
+ return new THREE.Vector3( this._x, this._y, this._z );
+
+ }
+
+ },
+
+ onChange: function ( callback ) {
+
+ this.onChangeCallback = callback;
+
+ return this;
+
+ },
+
+ onChangeCallback: function () {},
+
+ clone: function () {
+
+ return new THREE.Euler( this._x, this._y, this._z, this._order );
+
+ }
+
+};
+/*** END Euler ***/
+/*** START Math ***/
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Math = {
+
+ generateUUID: function () {
+
+ // http://www.broofa.com/Tools/Math.uuid.htm
+
+ var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' );
+ var uuid = new Array( 36 );
+ var rnd = 0, r;
+
+ return function () {
+
+ for ( var i = 0; i < 36; i ++ ) {
+
+ if ( i == 8 || i == 13 || i == 18 || i == 23 ) {
+
+ uuid[ i ] = '-';
+
+ } else if ( i == 14 ) {
+
+ uuid[ i ] = '4';
+
+ } else {
+
+ if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
+ r = rnd & 0xf;
+ rnd = rnd >> 4;
+ uuid[ i ] = chars[ ( i == 19 ) ? ( r & 0x3 ) | 0x8 : r ];
+
+ }
+ }
+
+ return uuid.join( '' );
+
+ };
+
+ }(),
+
+ // Clamp value to range
+
+ clamp: function ( x, a, b ) {
+
+ return ( x < a ) ? a : ( ( x > b ) ? b : x );
+
+ },
+
+ // Clamp value to range to range
+
+ mapLinear: function ( x, a1, a2, b1, b2 ) {
+
+ return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
+
+ },
+
+ // http://en.wikipedia.org/wiki/Smoothstep
+
+ smoothstep: function ( x, min, max ) {
+
+ if ( x <= min ) return 0;
+ if ( x >= max ) return 1;
+
+ x = ( x - min ) / ( max - min );
+
+ return x * x * ( 3 - 2 * x );
+
+ },
+
+ smootherstep: function ( x, min, max ) {
+
+ if ( x <= min ) return 0;
+ if ( x >= max ) return 1;
+
+ x = ( x - min ) / ( max - min );
+
+ return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
+
+ },
+
+ // Random float from <0, 1> with 16 bits of randomness
+ // (standard Math.random() creates repetitive patterns when applied over larger space)
+
+ random16: function () {
+
+ return ( 65280 * Math.random() + 255 * Math.random() ) / 65535;
+
+ },
+
+ // Random integer from interval
+
+ randInt: function ( low, high ) {
+
+ return Math.floor( this.randFloat( low, high ) );
+
+ },
+
+ // Random float from interval
+
+ randFloat: function ( low, high ) {
+
+ return low + Math.random() * ( high - low );
+
+ },
+
+ // Random float from <-range/2, range/2> interval
+
+ randFloatSpread: function ( range ) {
+
+ return range * ( 0.5 - Math.random() );
+
+ },
+
+ degToRad: function () {
+
+ var degreeToRadiansFactor = Math.PI / 180;
+
+ return function ( degrees ) {
+
+ return degrees * degreeToRadiansFactor;
+
+ };
+
+ }(),
+
+ radToDeg: function () {
+
+ var radianToDegreesFactor = 180 / Math.PI;
+
+ return function ( radians ) {
+
+ return radians * radianToDegreesFactor;
+
+ };
+
+ }(),
+
+ isPowerOfTwo: function ( value ) {
+
+ return ( value & ( value - 1 ) ) === 0 && value !== 0;
+
+ },
+
+ nextPowerOfTwo: function ( value ) {
+
+ value --;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ value ++;
+
+ return value;
+ }
+
+};
+
+/*** END Math ***/
+
+}
+
+module.exports = THREE;
+
+},{}],10:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var THREE = _dereq_('./three-math.js');
+var Util = _dereq_('./util.js');
+
+var ROTATE_SPEED = 0.5;
+/**
+ * Provides a quaternion responsible for pre-panning the scene before further
+ * transformations due to device sensors.
+ */
+function TouchPanner() {
+ window.addEventListener('touchstart', this.onTouchStart_.bind(this));
+ window.addEventListener('touchmove', this.onTouchMove_.bind(this));
+ window.addEventListener('touchend', this.onTouchEnd_.bind(this));
+
+ this.isTouching = false;
+ this.rotateStart = new THREE.Vector2();
+ this.rotateEnd = new THREE.Vector2();
+ this.rotateDelta = new THREE.Vector2();
+
+ this.theta = 0;
+ this.orientation = new THREE.Quaternion();
+}
+
+TouchPanner.prototype.getOrientation = function() {
+ this.orientation.setFromEuler(new THREE.Euler(0, 0, this.theta));
+ return this.orientation;
+};
+
+TouchPanner.prototype.resetSensor = function() {
+ this.theta = 0;
+};
+
+TouchPanner.prototype.onTouchStart_ = function(e) {
+ // Only respond if there is exactly one touch.
+ if (e.touches.length != 1) {
+ return;
+ }
+ this.rotateStart.set(e.touches[0].pageX, e.touches[0].pageY);
+ this.isTouching = true;
+};
+
+TouchPanner.prototype.onTouchMove_ = function(e) {
+ if (!this.isTouching) {
+ return;
+ }
+ this.rotateEnd.set(e.touches[0].pageX, e.touches[0].pageY);
+ this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart);
+ this.rotateStart.copy(this.rotateEnd);
+
+ // On iOS, direction is inverted.
+ if (Util.isIOS()) {
+ this.rotateDelta.x *= -1;
+ }
+
+ var element = document.body;
+ this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * ROTATE_SPEED;
+};
+
+TouchPanner.prototype.onTouchEnd_ = function(e) {
+ this.isTouching = false;
+};
+
+module.exports = TouchPanner;
+
+},{"./three-math.js":9,"./util.js":11}],11:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var Util = window.Util || {};
+
+Util.MIN_TIMESTEP = 0.001;
+Util.MAX_TIMESTEP = 1;
+
+Util.clamp = function(value, min, max) {
+ return Math.min(Math.max(min, value), max);
+};
+
+Util.isIOS = function() {
+ return /iPad|iPhone|iPod/.test(navigator.platform);
+};
+
+Util.isFirefoxAndroid = function() {
+ return navigator.userAgent.indexOf('Firefox') !== -1 && navigator.userAgent.indexOf('Android') !== -1;
+}
+
+// Helper method to validate the time steps of sensor timestamps.
+Util.isTimestampDeltaValid = function(timestampDeltaS) {
+ if (isNaN(timestampDeltaS)) {
+ return false;
+ }
+ if (timestampDeltaS <= Util.MIN_TIMESTEP) {
+ return false;
+ }
+ if (timestampDeltaS > Util.MAX_TIMESTEP) {
+ return false;
+ }
+ return true;
+}
+
+module.exports = Util;
+
+},{}],12:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var CardboardHMDVRDevice = _dereq_('./cardboard-hmd-vr-device.js');
+//var OrientationPositionSensorVRDevice = require('./orientation-position-sensor-vr-device.js');
+var FusionPositionSensorVRDevice = _dereq_('./fusion-position-sensor-vr-device.js');
+var MouseKeyboardPositionSensorVRDevice = _dereq_('./mouse-keyboard-position-sensor-vr-device.js');
+// Uncomment to add positional tracking via webcam.
+//var WebcamPositionSensorVRDevice = require('./webcam-position-sensor-vr-device.js');
+var HMDVRDevice = _dereq_('./base.js').HMDVRDevice;
+var PositionSensorVRDevice = _dereq_('./base.js').PositionSensorVRDevice;
+
+function WebVRPolyfill() {
+ this.devices = [];
+
+ if (!this.isWebVRAvailable()) {
+ this.enablePolyfill();
+ }
+}
+
+WebVRPolyfill.prototype.isWebVRAvailable = function() {
+ return ('getVRDevices' in navigator) || ('mozGetVRDevices' in navigator);
+};
+
+
+WebVRPolyfill.prototype.enablePolyfill = function() {
+ // Initialize our virtual VR devices.
+ if (this.isCardboardCompatible()) {
+ this.devices.push(new CardboardHMDVRDevice());
+ }
+
+ // Polyfill using the right position sensor.
+ if (this.isMobile()) {
+ //this.devices.push(new OrientationPositionSensorVRDevice());
+ this.devices.push(new FusionPositionSensorVRDevice());
+ } else {
+ if (!WebVRConfig.MOUSE_KEYBOARD_CONTROLS_DISABLED) {
+ this.devices.push(new MouseKeyboardPositionSensorVRDevice());
+ }
+ // Uncomment to add positional tracking via webcam.
+ //this.devices.push(new WebcamPositionSensorVRDevice());
+ }
+
+ // Provide navigator.getVRDevices.
+ navigator.getVRDevices = this.getVRDevices.bind(this);
+
+ // Provide the CardboardHMDVRDevice and PositionSensorVRDevice objects.
+ window.HMDVRDevice = HMDVRDevice;
+ window.PositionSensorVRDevice = PositionSensorVRDevice;
+};
+
+WebVRPolyfill.prototype.getVRDevices = function() {
+ var devices = this.devices;
+ return new Promise(function(resolve, reject) {
+ try {
+ resolve(devices);
+ } catch (e) {
+ reject(e);
+ }
+ });
+};
+
+/**
+ * Determine if a device is mobile.
+ */
+WebVRPolyfill.prototype.isMobile = function() {
+ return /Android/i.test(navigator.userAgent) ||
+ /iPhone|iPad|iPod/i.test(navigator.userAgent);
+};
+
+WebVRPolyfill.prototype.isCardboardCompatible = function() {
+ // For now, support all iOS and Android devices.
+ // Also enable the WebVRConfig.FORCE_VR flag for debugging.
+ return this.isMobile() || WebVRConfig.FORCE_ENABLE_VR;
+};
+
+module.exports = WebVRPolyfill;
+
+},{"./base.js":1,"./cardboard-hmd-vr-device.js":2,"./fusion-position-sensor-vr-device.js":4,"./mouse-keyboard-position-sensor-vr-device.js":6}]},{},[5]);
+
+},{}],8:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var MODE_LABELS = {
+ 0: 'UNKNOWN',
+ 1: 'NORMAL',
+ 2: 'MAGIC_WINDOW',
+ 3: 'VR'
+};
+
+function Analytics() {
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+ ga('create', 'UA-35315454-8', 'auto');
+ ga('send', 'pageview');
+
+ this.lastModeChangeTime = window.performance.now();
+ this.lastModeLabel = WebVRManager.Modes.UNKNOWN;
+}
+
+Analytics.prototype.logModeChanged = function(mode) {
+ var modeLabel = MODE_LABELS[mode];
+ var lastModeLabel = MODE_LABELS[this.lastMode];
+
+ console.log('Analytics: going from mode %s to %s', lastModeLabel, modeLabel);
+
+ ga('send', 'screenview', {
+ appName: 'EmbedVR',
+ screenName: modeLabel
+ });
+
+ var now = window.performance.now();
+ var msSinceLastModeChange = Math.round(now - this.lastModeChangeTime);
+ ga('send', 'timing', 'Time spent in mode', lastModeLabel, msSinceLastModeChange);
+
+ this.lastModeChangeTime = now;
+ this.lastMode = mode;
+}
+
+window.analytics = new Analytics();
+
+},{}],9:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Sits in an embedded iframe, receiving DeviceMotion messages from a containing
+ * iFrame. These messages are converted into synthetic DeviceMotion events.
+ *
+ * This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=150072.
+ */
+function DeviceMotionReceiver() {
+ window.addEventListener('message', this.onMessage_.bind(this), false);
+}
+
+DeviceMotionReceiver.prototype.onMessage_ = function(event) {
+ var message = event.data;
+ if (message.type !== 'DeviceMotion') {
+ console.warn('Got unknown message of type %s from %s', message.type, message.origin);
+ return;
+ }
+
+ console.log('onMessage_', event);
+
+ // Synthesize a DeviceMotion event.
+ this.synthesizeDeviceMotionEvent_(message.deviceMotionEvent);
+};
+
+DeviceMotionReceiver.prototype.synthesizeDeviceMotionEvent_ = function(eventData) {
+ var type = 'devicemotion-iframe';
+ var canBubble = false;
+ var cancelable = false;
+
+ var dme = document.createEvent('DeviceMotionEvent');
+ dme.initDeviceMotionEvent(type, canBubble, cancelable,
+ eventData.acceleration,
+ eventData.accelerationIncludingGravity,
+ eventData.rotationRate,
+ eventData.interval);
+
+ window.dispatchEvent(dme);
+};
+
+module.exports = DeviceMotionReceiver;
+
+},{}],10:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+function Emitter() {
+ this.initEmitter();
+}
+
+Emitter.prototype.initEmitter = function() {
+ this.callbacks = {};
+};
+
+Emitter.prototype.emit = function(eventName) {
+ var callbacks = this.callbacks[eventName];
+ if (!callbacks) {
+ console.log('No valid callback specified.');
+ return;
+ }
+ var args = [].slice.call(arguments)
+ // Eliminate the first param (the callback).
+ args.shift();
+ for (var i = 0; i < callbacks.length; i++) {
+ callbacks[i].apply(this, args);
+ }
+};
+
+Emitter.prototype.on = function(eventName, callback) {
+ if (eventName in this.callbacks) {
+ var cbs = this.callbacks[eventName]
+ if (cbs.indexOf(callback) == -1) {
+ cbs.push(callback);
+ }
+ } else {
+ this.callbacks[eventName] = [callback];
+ }
+};
+
+Emitter.prototype.removeListener = function(eventName, callback) {
+ if (!(eventName in this.callbacks)) {
+ return;
+ }
+ var cbs = this.callbacks[eventName];
+ var ind = cbs.indexOf(callback);
+ if (ind == -1) {
+ console.warn('No matching callback found');
+ return;
+ }
+ cbs.splice(ind, 1);
+};
+
+module.exports = Emitter;
+
+},{}],11:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var Eyes = {
+ LEFT: 0,
+ RIGHT: 1
+};
+
+module.exports = Eyes;
+
+},{}],12:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Shows a 2D loading indicator while various pieces of EmbedVR load.
+ */
+function LoadingIndicator() {
+ this.el = this.build_();
+ document.body.appendChild(this.el);
+ this.show();
+}
+
+LoadingIndicator.prototype.build_ = function() {
+ var overlay = document.createElement('div');
+ var s = overlay.style;
+ s.position = 'fixed';
+ s.top = 0;
+ s.left = 0;
+ s.width = '100%';
+ s.height = '100%';
+ s.background = '#eee';
+ var img = document.createElement('img');
+ img.src = 'images/loading.gif';
+ var s = img.style;
+ s.position = 'absolute';
+ s.top = '50%';
+ s.left = '50%';
+ s.transform = 'translate(-50%, -50%)';
+
+ overlay.appendChild(img);
+ return overlay;
+};
+
+LoadingIndicator.prototype.hide = function() {
+ this.el.style.display = 'none';
+};
+
+LoadingIndicator.prototype.show = function() {
+ this.el.style.display = 'block';
+};
+
+module.exports = LoadingIndicator;
+
+},{}],13:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Disable distortion provided by the boilerplate because we are doing
+// vertex-based distortion.
+WebVRConfig = window.WebVRConfig || {}
+WebVRConfig.PREVENT_DISTORTION = true;
+
+// Initialize the loading indicator as quickly as possible to give the user
+// immediate feedback.
+var LoadingIndicator = _dereq_('./loading-indicator');
+var loadIndicator = new LoadingIndicator();
+
+// Include relevant polyfills.
+_dereq_('../node_modules/webvr-polyfill/build/webvr-polyfill');
+var ES6Promise = _dereq_('../node_modules/es6-promise/dist/es6-promise.min');
+// Polyfill ES6 promises for IE.
+ES6Promise.polyfill();
+
+var PhotosphereRenderer = _dereq_('./photosphere-renderer');
+var SceneLoader = _dereq_('./scene-loader');
+var Stats = _dereq_('../node_modules/stats-js/build/stats.min');
+var Util = _dereq_('./util');
+
+// Include the DeviceMotionReceiver for the iOS cross domain iframe workaround.
+// This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=150072.
+var DeviceMotionReceiver = _dereq_('./device-motion-receiver');
+var dmr = new DeviceMotionReceiver();
+
+
+window.addEventListener('load', init);
+
+var stats = new Stats();
+
+var loader = new SceneLoader();
+loader.on('error', onSceneError);
+loader.on('load', onSceneLoad);
+
+var renderer = new PhotosphereRenderer();
+renderer.on('error', onRenderError);
+
+var videoElement = null;
+// TODO: Make this not global.
+// Currently global in order to allow callbacks.
+var loadedScene = null;
+
+function init() {
+ if (!Util.isWebGLEnabled()) {
+ showError('WebGL not supported.');
+ return;
+ }
+ // Load the scene.
+ loader.loadScene();
+
+ if (Util.getQueryParameter('debug')) {
+ showStats();
+ }
+}
+
+function loadImage(src, params) {
+ renderer.on('load', onRenderLoad);
+ renderer.setPhotosphere(src, params);
+}
+
+function onSceneLoad(scene) {
+ if (!scene || !scene.isComplete()) {
+ showError('Scene failed to load');
+ return;
+ }
+
+ loadedScene = scene;
+
+ var params = {
+ isStereo: scene.isStereo,
+ }
+ renderer.setDefaultLookDirection(scene.yaw || 0);
+
+ if (scene.preview) {
+ var onPreviewLoad = function() {
+ loadIndicator.hide();
+ renderer.removeListener('load', onPreviewLoad);
+ renderer.setPhotosphere(scene.image, params);
+ }
+ renderer.removeListener('load', onRenderLoad);
+ renderer.on('load', onPreviewLoad);
+ renderer.setPhotosphere(scene.preview, params);
+ } else if (scene.video) {
+ if (Util.isIOS() || Util.isIE11()) {
+ // On iOS and IE 11, if an 'image' param is provided, load it instead of
+ // showing an error.
+ //
+ // TODO(smus): Once video textures are supported, remove this fallback.
+ if (scene.image) {
+ loadImage(scene.image, params);
+ } else {
+ showError('Video is not supported on this platform (iOS or IE11).');
+ }
+ } else {
+ // Load the video element.
+ videoElement = document.createElement('video');
+ videoElement.src = scene.video;
+ videoElement.loop = true;
+ videoElement.setAttribute('crossorigin', 'anonymous');
+ videoElement.addEventListener('canplaythrough', onVideoLoad);
+ videoElement.addEventListener('error', onVideoError);
+ }
+ } else if (scene.image) {
+ // Otherwise, just render the photosphere.
+ loadImage(scene.image, params);
+ }
+
+ console.log('Loaded scene', scene);
+}
+
+function onVideoLoad() {
+ // Render the stereo video.
+ var params = {
+ isStereo: loadedScene.isStereo,
+ }
+ renderer.set360Video(videoElement, params);
+
+ // On mobile, tell the user they need to tap to start. Otherwise, autoplay.
+ if (!Util.isMobile()) {
+ // Hide loading indicator.
+ loadIndicator.hide();
+ // Autoplay the video on desktop.
+ videoElement.play();
+ } else {
+ // Tell user to tap to start.
+ showError('Tap to start video', 'Play');
+ document.body.addEventListener('touchend', onVideoTap);
+ }
+
+ // Prevent onVideoLoad from firing multiple times.
+ videoElement.removeEventListener('canplaythrough', onVideoLoad);
+}
+
+function onVideoTap() {
+ hideError();
+ videoElement.play();
+
+ // Prevent multiple play() calls on the video element.
+ document.body.removeEventListener('touchend', onVideoTap);
+}
+
+function onRenderLoad() {
+ // Hide loading indicator.
+ loadIndicator.hide();
+}
+
+function onSceneError(message) {
+ showError('Loader: ' + message);
+}
+
+function onRenderError(message) {
+ showError('Render: ' + message);
+}
+
+function onVideoError(e) {
+ showError('Video load error');
+ console.log(e);
+}
+
+function showError(message, opt_title) {
+ // Hide loading indicator.
+ loadIndicator.hide();
+
+ var error = document.querySelector('#error');
+ error.classList.add('visible');
+ error.querySelector('.message').innerHTML = message;
+
+ var title = (opt_title !== undefined ? opt_title : 'Error');
+ error.querySelector('.title').innerHTML = title;
+}
+
+function hideError() {
+ var error = document.querySelector('#error');
+ error.classList.remove('visible');
+}
+
+function showStats() {
+ stats.setMode(0); // 0: fps, 1: ms
+
+ // Align bottom-left.
+ stats.domElement.style.position = 'absolute';
+ stats.domElement.style.left = '0px';
+ stats.domElement.style.bottom = '0px';
+ document.body.appendChild(stats.domElement);
+}
+
+function loop(time) {
+ stats.begin();
+ renderer.render(time);
+ stats.end();
+ requestAnimationFrame(loop);
+}
+requestAnimationFrame(loop);
+
+},{"../node_modules/es6-promise/dist/es6-promise.min":1,"../node_modules/stats-js/build/stats.min":2,"../node_modules/webvr-polyfill/build/webvr-polyfill":7,"./device-motion-receiver":9,"./loading-indicator":12,"./photosphere-renderer":14,"./scene-loader":16,"./util":17}],14:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var Emitter = _dereq_('./emitter');
+var Eyes = _dereq_('./eyes');
+var THREE = _dereq_('../node_modules/three/three');
+THREE.VRControls = _dereq_('../node_modules/three/examples/js/controls/VRControls');
+THREE.VREffect = _dereq_('../node_modules/three/examples/js/effects/VREffect');
+var Util = _dereq_('./util');
+var VertexDistorter = _dereq_('./vertex-distorter');
+_dereq_('../node_modules/webvr-boilerplate/build/webvr-manager');
+
+function PhotosphereRenderer() {
+ this.init();
+}
+PhotosphereRenderer.prototype = new Emitter();
+
+PhotosphereRenderer.prototype.init = function() {
+ var container = document.querySelector('body');
+ var camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 100);
+ var cameraDummy = new THREE.Object3D();
+ cameraDummy.add(camera);
+
+ // Antialiasing temporarily disabled to improve performance.
+ var renderer = new THREE.WebGLRenderer({antialias: false});
+ renderer.setClearColor(0x000000, 0);
+ renderer.setSize(window.innerWidth, window.innerHeight);
+
+ // Round down fractional DPR values for better performance.
+ renderer.setPixelRatio(Math.floor(window.devicePixelRatio));
+ container.appendChild(renderer.domElement);
+
+ var controls = new THREE.VRControls(camera);
+ var effect = new THREE.VREffect(renderer);
+ effect.setSize(window.innerWidth, window.innerHeight);
+
+ this.camera = camera;
+ this.renderer = renderer;
+ this.effect = effect;
+ this.controls = controls;
+ this.manager = new WebVRManager(renderer, effect, {isUndistorted: true});
+
+ this.initScenes_();
+
+ // The vertex distorter.
+ this.distorter = new VertexDistorter(this.manager.getDeviceInfo());
+
+ this.manager.on('modechange', this.onModeChange_.bind(this));
+ this.manager.on('viewerchange', this.onViewerChange_.bind(this));
+
+ // Watch the resize event.
+ window.addEventListener('resize', this.onResize_.bind(this));
+
+ var that = this;
+ navigator.getVRDevices().then(function(devices) {
+ devices.forEach(function(device) {
+ if (device instanceof HMDVRDevice) {
+ that.hmd = device;
+ }
+ });
+ });
+};
+
+PhotosphereRenderer.prototype.render = function(timestamp) {
+ this.controls.update();
+ this.manager.render(this.scenes, this.camera, timestamp);
+};
+
+PhotosphereRenderer.prototype.setDefaultLookDirection = function(phi) {
+ // Rotate the camera parent to take into account the scene's rotation.
+ this.camera.parent.rotation.y = phi;
+};
+
+/**
+ * Sets the photosphere based on the image in the source. Supports stereo and
+ * mono photospheres.
+ *
+ * Emits 'load' and 'error' events.
+ */
+PhotosphereRenderer.prototype.setPhotosphere = function(src, opt_params) {
+ var params = opt_params || {};
+
+ this.isStereo = !!params.isStereo;
+ this.src = src;
+
+ // Load texture.
+ var loader = new THREE.TextureLoader();
+ loader.crossOrigin = 'anonymous';
+ loader.load(src, this.onTextureLoaded_.bind(this), null,
+ this.onTextureError_.bind(this));
+};
+
+PhotosphereRenderer.prototype.set360Video = function(videoElement, opt_params) {
+ var params = opt_params || {};
+
+ this.isStereo = !!params.isStereo;
+
+ // Load the video texture.
+ var videoTexture = new THREE.VideoTexture(videoElement);
+ videoTexture.minFilter = THREE.LinearFilter;
+ videoTexture.magFilter = THREE.LinearFilter;
+ videoTexture.format = THREE.RGBFormat;
+ videoTexture.generateMipmaps = false;
+ videoTexture.needsUpdate = true;
+
+ this.onTextureLoaded_(videoTexture);
+};
+
+PhotosphereRenderer.prototype.initScenes_ = function() {
+ this.sceneLeft = this.createScene_();
+ this.sceneRight = this.createScene_();
+ this.sceneLeft.add(this.camera.parent);
+
+ this.scenes = [this.sceneLeft, this.sceneRight];
+ this.eyes = [Eyes.LEFT, Eyes.RIGHT];
+};
+
+PhotosphereRenderer.prototype.onTextureLoaded_ = function(texture) {
+ var sphereLeft;
+ var sphereRight;
+ if (this.isStereo) {
+ sphereLeft = this.createPhotosphere_(texture, {offsetY: 0.5, scaleY: 0.5});
+ sphereRight = this.createPhotosphere_(texture, {offsetY: 0, scaleY: 0.5});
+ } else {
+ sphereLeft = this.createPhotosphere_(texture);
+ sphereRight = this.createPhotosphere_(texture);
+ }
+
+ this.sceneLeft.getObjectByName('photo').children = [sphereLeft];
+ this.sceneRight.getObjectByName('photo').children = [sphereRight];
+
+ this.emit('load');
+};
+
+PhotosphereRenderer.prototype.onTextureError_ = function(error) {
+ this.emit('error', 'Unable to load texture from ' + this.src);
+};
+
+
+PhotosphereRenderer.prototype.createPhotosphere_ = function(texture, opt_params) {
+ var p = opt_params || {};
+ p.scaleX = p.scaleX || 1;
+ p.scaleY = p.scaleY || 1;
+ p.offsetX = p.offsetX || 0;
+ p.offsetY = p.offsetY || 0;
+ p.phiStart = p.phiStart || 0;
+ p.phiLength = p.phiLength || Math.PI * 2;
+ p.thetaStart = p.thetaStart || 0;
+ p.thetaLength = p.thetaLength || Math.PI;
+
+ var geometry = new THREE.SphereGeometry(1, 48, 48,
+ p.phiStart, p.phiLength, p.thetaStart, p.thetaLength);
+ geometry.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
+ var uvs = geometry.faceVertexUvs[0];
+ for (var i = 0; i < uvs.length; i ++) {
+ for (var j = 0; j < 3; j ++) {
+ uvs[i][j].x *= p.scaleX;
+ uvs[i][j].x += p.offsetX;
+ uvs[i][j].y *= p.scaleY;
+ uvs[i][j].y += p.offsetY;
+ }
+ }
+
+ var material = new THREE.MeshBasicMaterial({ map: texture });
+ this.distorter.setMap(texture);
+ var out = new THREE.Mesh(geometry, material);
+ out.renderOrder = -1;
+ return out;
+};
+
+PhotosphereRenderer.prototype.createScene_ = function(opt_params) {
+ var scene = new THREE.Scene();
+ // Add a light.
+ scene.add(new THREE.PointLight(0xFFFFFF));
+
+ // Add a group for the photosphere.
+ var photoGroup = new THREE.Object3D();
+ photoGroup.name = 'photo';
+ scene.add(photoGroup);
+
+ return scene;
+};
+
+PhotosphereRenderer.prototype.updateMaterial_ = function(material_FOO) {
+ for (var i = 0; i < this.scenes.length; i++) {
+ var eye = this.eyes[i];
+ var material = this.distorter.getShaderMaterial(eye);
+ var scene = this.scenes[i];
+ var children = scene.getObjectByName('photo').children;
+ for (var j = 0; j < children.length; j++) {
+ var child = children[j];
+ child.material = material;
+ child.material.needsUpdate = true;
+ }
+ }
+};
+
+PhotosphereRenderer.prototype.updateRenderRect_ = function() {
+ if (this.hmd && this.hmd.setRenderRect) {
+ var deviceInfo = this.manager.getDeviceInfo();
+ var leftRect = deviceInfo.getUndistortedViewportLeftEye();
+ var dpr = window.devicePixelRatio;
+ leftRect.x /= dpr;
+ leftRect.y /= dpr;
+ leftRect.width /= dpr;
+ leftRect.height /= dpr;
+
+ var rightRect = Util.clone(leftRect);
+ rightRect.x = (window.innerWidth - leftRect.x) - leftRect.width;
+
+ this.hmd.setRenderRect(leftRect, rightRect);
+ }
+};
+
+PhotosphereRenderer.prototype.onModeChange_ = function(newMode, oldMode) {
+ console.log('onModeChange_', newMode);
+
+ var coefficients;
+ if (newMode == WebVRManager.Modes.VR) {
+ // Entering VR mode.
+ this.distorter.setEnabled(true);
+ this.updateMaterial_();
+ } else if (oldMode == WebVRManager.Modes.VR) {
+ // Leaving VR mode.
+ this.distorter.setEnabled(false);
+ this.updateMaterial_();
+ }
+
+ if (window.analytics) {
+ analytics.logModeChanged(newMode);
+ }
+};
+
+PhotosphereRenderer.prototype.onViewerChange_ = function(newViewer) {
+ console.log('onViewerChange_', newViewer);
+
+ // Reset the photosphere with new coefficients.
+ this.updateMaterial_();
+ this.updateRenderRect_();
+};
+
+PhotosphereRenderer.prototype.onResize_ = function() {
+ this.updateRenderRect_();
+};
+
+module.exports = PhotosphereRenderer;
+
+},{"../node_modules/three/examples/js/controls/VRControls":3,"../node_modules/three/examples/js/effects/VREffect":4,"../node_modules/three/three":5,"../node_modules/webvr-boilerplate/build/webvr-manager":6,"./emitter":10,"./eyes":11,"./util":17,"./vertex-distorter":18}],15:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Contains all information about a given scene, including the photosphere asset,
+ * background music.
+ */
+function SceneInfo(opt_params) {
+ var params = opt_params || {};
+ this.id = params.id;
+ this.title = params.title;
+ this.image = params.image;
+ this.preview = params.preview;
+ this.isStereo = !!params.isStereo;
+ this.audio = params.audio;
+ this.video = params.video;
+ this.yaw = params.yaw || 0;
+ this.isYawOnly = params.isYawOnly;
+}
+
+SceneInfo.prototype.isComplete = function() {
+ return !!this.image || !!this.video;
+};
+
+SceneInfo.prototype.toObject = function() {
+ return {
+ id: this.id || null,
+ title: this.title || null,
+ image: this.image,
+ preview: this.preview,
+ isStereo: this.isStereo,
+ audio: this.audio,
+ yaw: this.yaw || null,
+ video: this.video || null,
+ };
+};
+
+SceneInfo.prototype.isImageDataURI = function() {
+ return this.image.indexOf('data:') == 0;
+};
+
+
+module.exports = SceneInfo;
+
+},{}],16:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var Emitter = _dereq_('./emitter');
+var SceneInfo = _dereq_('./scene-info');
+
+var Query = {
+ JSON_URL: 'url',
+ VIDEO_URL: 'video',
+ OBJECT_URL: 'object',
+ IMAGE_URL: 'image',
+ PREVIEW_URL: 'preview',
+ IS_STEREO: 'is_stereo',
+ AUDIO_URL: 'audio',
+ START_YAW: 'start_yaw',
+ IS_YAW_ONLY: 'is_yaw_only',
+};
+
+function SceneLoader() {
+}
+SceneLoader.prototype = new Emitter();
+
+/**
+ * Loads a scene from a JSON file.
+ */
+SceneLoader.prototype.loadFromJson_ = function(url, callback) {
+ // XHR to fetch the JSON.
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url);
+ var that = this;
+ xhr.onload = function(e) {
+ try {
+ var jsonObj = JSON.parse(this.response);
+ } catch (e) {
+ that.emit('error', 'Invalid JSON at ' + url + '.');
+ return;
+ }
+ var labelObjects = {};
+ var labels = jsonObj.labels || [];
+ // Go through the labels in the data and objectify them.
+ for (var i = 0; i < labels.length; i++) {
+ var label = new Label(labels[i]);
+ labelObjects[label.id] = label;
+ }
+ jsonObj.labels = labelObjects;
+ var scene = new SceneInfo(jsonObj);
+ that.emit('load', scene);
+ };
+ xhr.send();
+};
+
+/**
+ * Parse out GET parameters from the URL string and load the scene.
+ */
+SceneLoader.prototype.loadFromGetParams_ = function() {
+ var params = {
+ image: Util.getQueryParameter(Query.IMAGE_URL),
+ video: Util.getQueryParameter(Query.VIDEO_URL),
+ object: Util.getQueryParameter(Query.OBJECT_URL),
+ preview: Util.getQueryParameter(Query.PREVIEW_URL),
+ isStereo: this.parseBoolean_(Util.getQueryParameter(Query.IS_STEREO)),
+ audio: Util.getQueryParameter(Query.AUDIO_URL),
+ isYawOnly: this.parseBoolean_(Util.getQueryParameter(Query.IS_YAW_ONLY)),
+ yaw: THREE.Math.degToRad(Util.getQueryParameter(Query.START_YAW)),
+ };
+
+ var count = 0;
+ count += (params[Query.IMAGE_URL] ? 1 : 0);
+ count += (params[Query.VIDEO_URL] ? 1 : 0);
+ count += (params[Query.OBJECT_URL] ? 1 : 0);
+ // Validate this.
+ if (count == 0) {
+ this.emit('error', 'Either "image", "video", or "object" GET parameter is required.');
+ return;
+ }
+
+ var scene = new SceneInfo(params);
+ this.emit('load', scene);
+};
+
+SceneLoader.prototype.parseBoolean_ = function(value) {
+ if (value == 'false') {
+ return false;
+ }
+ return !!value;
+};
+
+SceneLoader.prototype.loadScene = function(callback) {
+ // If there's a url param specified, try loading from JSON.
+ var url = Util.getQueryParameter('url');
+ var image = Util.getQueryParameter('image');
+ var video = Util.getQueryParameter('video');
+ var object = Util.getQueryParameter('object');
+ if (url) {
+ this.loadFromJson_(url);
+ } else if (image || video || object) {
+ // Otherwise, try loading from URL parameters.
+ this.loadFromGetParams_();
+ } else {
+ // If it fails, throw an exception.
+ this.emit('error', 'Unable to load scene. Required parameter missing.');
+ }
+};
+
+module.exports = SceneLoader;
+
+},{"./emitter":10,"./scene-info":15}],17:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+Util = window.Util || {};
+
+Util.isDataURI = function(src) {
+ return src && src.indexOf('data:') == 0;
+};
+
+Util.generateUUID = function() {
+ function s4() {
+ return Math.floor((1 + Math.random()) * 0x10000)
+ .toString(16)
+ .substring(1);
+ }
+ return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
+ s4() + '-' + s4() + s4() + s4();
+};
+
+Util.isMobile = function() {
+ var check = false;
+ (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
+ return check;
+};
+
+Util.isIOS = function() {
+ return /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
+};
+
+Util.cloneObject = function(obj) {
+ var out = {};
+ for (key in obj) {
+ out[key] = obj[key];
+ }
+ return out;
+};
+
+Util.hashCode = function(s) {
+ return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);
+};
+
+Util.loadTrackSrc = function(context, src, callback, opt_progressCallback) {
+ var request = new XMLHttpRequest();
+ request.open('GET', src, true);
+ request.responseType = 'arraybuffer';
+
+ // Decode asynchronously.
+ request.onload = function() {
+ context.decodeAudioData(request.response, function(buffer) {
+ callback(buffer);
+ }, function(e) {
+ console.error(e);
+ });
+ };
+ if (opt_progressCallback) {
+ request.onprogress = function(e) {
+ var percent = e.loaded / e.total;
+ opt_progressCallback(percent);
+ };
+ }
+ request.send();
+};
+
+Util.isPow2 = function(n) {
+ return (n & (n - 1)) == 0;
+};
+
+Util.capitalize = function(s) {
+ return s.charAt(0).toUpperCase() + s.slice(1);
+};
+
+Util.isIFrame = function() {
+ try {
+ return window.self !== window.top;
+ } catch (e) {
+ return true;
+ }
+};
+
+// From http://goo.gl/4WX3tg
+Util.getQueryParameter = function(name) {
+ name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
+ var regex = new RegExp("[\\?&]" + name + "=([^]*)"),
+ results = regex.exec(location.search);
+ return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
+};
+
+
+// From http://stackoverflow.com/questions/11871077/proper-way-to-detect-webgl-support.
+Util.isWebGLEnabled = function() {
+ var canvas = document.createElement('canvas');
+ try { gl = canvas.getContext("webgl"); }
+ catch (x) { gl = null; }
+
+ if (gl == null) {
+ try { gl = canvas.getContext("experimental-webgl"); experimental = true; }
+ catch (x) { gl = null; }
+ }
+ return !!gl;
+};
+
+Util.clone = function(obj) {
+ return JSON.parse(JSON.stringify(obj));
+};
+
+// From http://stackoverflow.com/questions/10140604/fastest-hypotenuse-in-javascript
+Util.hypot = Math.hypot || function(x, y) {
+ return Math.sqrt(x*x + y*y);
+};
+
+// From http://stackoverflow.com/a/17447718/693934
+Util.isIE11 = function() {
+ return navigator.userAgent.match(/Trident/);
+};
+
+module.exports = Util;
+
+},{}],18:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+THREE = _dereq_('../node_modules/three/three');
+var Eyes = _dereq_('./eyes');
+var Util = _dereq_('./util');
+
+var DEFAULT_FOV = 40;
+var NO_DISTORTION = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+/**
+ * Responsible for distortion correction by pre-distorting the vertex geometry
+ * so that a second shader pass is not needed. This is intended to greatly
+ * optimize the rendering process.
+ */
+function VertexDistorter(deviceInfo) {
+ this.texture = null;
+ this.isEnabled = false;
+
+ this.deviceInfo = deviceInfo;
+}
+
+VertexDistorter.prototype.setEnabled = function(isEnabled) {
+ this.isEnabled = isEnabled;
+}
+
+/**
+ * Sets the texture that is used to render this photosphere.
+ */
+VertexDistorter.prototype.setMap = function(texture) {
+ this.texture = texture;
+};
+
+VertexDistorter.prototype.getVertexShader_ = function() {
+ return [
+ '#ifdef GL_ES',
+ 'precision highp float;',
+ '#endif',
+
+ 'varying vec2 vUV;',
+
+ this.getDistortionInclude_(),
+
+ 'void main() {',
+ // Pass through texture coordinates to the fragment shader.
+ 'vUV = uv;',
+
+ // Here, we want to ensure that we are using an undistorted projection
+ // matrix. By setting isUndistorted: true in the WebVRManager, we
+ // guarantee this.
+ 'vec4 pos = projectionMatrix * modelViewMatrix * vec4(position, 1.0);',
+
+ 'gl_Position = Distort(pos);',
+ '}'
+ ].join('\n');
+};
+
+VertexDistorter.prototype.getNoopVertexShader_ = function() {
+ return [
+ '#ifdef GL_ES',
+ 'precision highp float;',
+ '#endif',
+
+ 'varying vec2 vUV;',
+
+ 'void main() {',
+ 'gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);',
+ 'vUV = uv;',
+ '}'
+ ].join('\n');
+};
+
+VertexDistorter.prototype.getFragmentShader_ = function() {
+ return [
+ '#ifdef GL_ES',
+ 'precision highp float;',
+ '#endif',
+
+ 'varying vec2 vUV;',
+ 'uniform sampler2D texture;',
+
+ 'void main() {',
+ 'gl_FragColor = texture2D(texture, vUV);',
+ '}'
+ ].join('\n');
+};
+
+VertexDistorter.prototype.getNoopDistortionInclude_ = function() {
+ return [
+ 'vec4 Distort(vec4 point) {',
+ 'return point;',
+ '}'
+ ].join('\n');
+};
+
+VertexDistorter.prototype.getDistortionInclude_ = function() {
+ return [
+ 'uniform float uDistortionCoefficients[12];',
+ 'uniform float uDistortionMaxFovSquared;',
+ 'uniform vec2 uDistortionFovOffset;',
+ 'uniform vec2 uDistortionFovScale;',
+
+ // Returns a scalar to distort a point; computed in reverse via the polynomial approximation:
+ // r' = 1 + Σ_i (uDistortionCoefficients[i] rSquared^(i+1)) i=[0..11]
+ // where rSquared is the squared radius of an undistorted point in tan-angle space.
+ // See {@link Distortion} for more information.
+ 'float DistortionFactor(float rSquared) {',
+ 'float ret = 0.0;',
+ 'rSquared = min(uDistortionMaxFovSquared, rSquared);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[11]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[10]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[9]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[8]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[7]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[6]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[5]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[4]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[3]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[2]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[1]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[0]);',
+ 'return ret + 1.0;',
+ '}',
+
+ // Given a point in clip space, distort the point according to the coefficients stored in
+ // uDistortionCoefficients and the field of view (FOV) specified in uDistortionFovOffset and
+ // uDistortionFovScale.
+ // Returns the distorted point in clip space, with its Z untouched.
+ 'vec4 Distort(vec4 point) {',
+ // Put point into normalized device coordinates (NDC), [(-1, -1, -1) to (1, 1, 1)].
+ 'vec3 pointNdc = point.xyz / point.w;',
+ // Throw away the Z coordinate and map the point to the unit square, [(0, 0) to (1, 1)].
+ 'vec2 pointUnitSquare = (pointNdc.xy + vec2(1.0)) / 2.0;',
+ // Map the point into FOV tan-angle space.
+ 'vec2 pointTanAngle = pointUnitSquare * uDistortionFovScale - uDistortionFovOffset;',
+ 'float radiusSquared = dot(pointTanAngle, pointTanAngle);',
+ 'float distortionFactor = DistortionFactor(radiusSquared);',
+ //'float distortionFactor = 2.0;',
+ 'vec2 distortedPointTanAngle = pointTanAngle * distortionFactor;',
+ // Reverse the mappings above to bring the distorted point back into NDC space.
+ 'vec2 distortedPointUnitSquare = (distortedPointTanAngle + uDistortionFovOffset)',
+ '/ uDistortionFovScale;',
+ 'vec3 distortedPointNdc = vec3(distortedPointUnitSquare * 2.0 - vec2(1.0), pointNdc.z);',
+ // Convert the point into clip space before returning in case any operations are done after.
+ 'return vec4(distortedPointNdc, 1.0) * point.w;',
+ '}',
+ ].join('\n');
+};
+
+VertexDistorter.prototype.getDistortionMaxFovSquared_ = function() {
+ var fov = this.getFov_();
+ var maxFov = Util.hypot(
+ Math.tan(THREE.Math.degToRad(Math.max(fov.leftDegrees, fov.rightDegrees))),
+ Math.tan(THREE.Math.degToRad(Math.max(fov.downDegrees, fov.upDegrees))));
+ return maxFov * maxFov;
+};
+
+VertexDistorter.prototype.getDistortionCoefficients_ = function() {
+ var viewer = this.deviceInfo.viewer;
+ return this.isEnabled ? viewer.inverseCoefficients : NO_DISTORTION;
+};
+
+VertexDistorter.prototype.getDistortionFovOffset_ = function(eye) {
+ var fov = this.getFov_(eye);
+ /*
+ var sideDegrees = Math.min(fov.leftDegrees, fov.rightDegrees);
+ var left = Math.tan(THREE.Math.degToRad(sideDegrees));
+ */
+ var left = Math.tan(THREE.Math.degToRad(fov.leftDegrees));
+ var down = Math.tan(THREE.Math.degToRad(fov.downDegrees));
+ return new THREE.Vector2(left, down);
+};
+
+VertexDistorter.prototype.getDistortionFovScale_ = function() {
+ var fov = this.getFov_();
+ var left = Math.tan(THREE.Math.degToRad(fov.leftDegrees));
+ var right = Math.tan(THREE.Math.degToRad(fov.rightDegrees));
+ var up = Math.tan(THREE.Math.degToRad(fov.upDegrees));
+ var down = Math.tan(THREE.Math.degToRad(fov.downDegrees));
+ return new THREE.Vector2(left + right, up + down);
+};
+
+
+VertexDistorter.prototype.getUniforms_ = function(eye) {
+ return {
+ texture: {
+ type: 't',
+ value: this.texture
+ },
+ uDistortionCoefficients: {
+ type: 'fv1',
+ value: this.getDistortionCoefficients_()
+ },
+ uDistortionMaxFovSquared: {
+ type: 'f',
+ value: this.getDistortionMaxFovSquared_()
+ },
+ uDistortionFovOffset: {
+ type: 'v2',
+ value: this.getDistortionFovOffset_(eye)
+ },
+ uDistortionFovScale: {
+ type: 'v2',
+ value: this.getDistortionFovScale_()
+ }
+ };
+};
+
+VertexDistorter.prototype.getShaderMaterial = function(eye) {
+ var uniforms = this.getUniforms_(eye);
+ return new THREE.ShaderMaterial({
+ uniforms: uniforms,
+ vertexShader: this.getVertexShader_(),
+ fragmentShader: this.getFragmentShader_()
+ });
+};
+
+VertexDistorter.prototype.getFov_ = function(opt_eye) {
+ var eye = opt_eye || Eyes.LEFT;
+
+ if (eye == Eyes.LEFT) {
+ return this.deviceInfo.getFieldOfViewLeftEye(true);
+ }
+ if (eye == Eyes.RIGHT) {
+ return this.deviceInfo.getFieldOfViewRightEye(true);
+ }
+ return null;
+};
+
+module.exports = VertexDistorter;
+
+},{"../node_modules/three/three":5,"./eyes":11,"./util":17}],19:[function(_dereq_,module,exports){
+// Load EmbedVR.
+_dereq_('./main');
+
+// Load Analytics for EmbedVR.
+_dereq_('./analytics');
+
+},{"./analytics":8,"./main":13}],20:[function(_dereq_,module,exports){
+// shim for using process in browser
+
+var process = module.exports = {};
+var queue = [];
+var draining = false;
+var currentQueue;
+var queueIndex = -1;
+
+function cleanUpNextTick() {
+ draining = false;
+ if (currentQueue.length) {
+ queue = currentQueue.concat(queue);
+ } else {
+ queueIndex = -1;
+ }
+ if (queue.length) {
+ drainQueue();
+ }
+}
+
+function drainQueue() {
+ if (draining) {
+ return;
+ }
+ var timeout = setTimeout(cleanUpNextTick);
+ draining = true;
+
+ var len = queue.length;
+ while(len) {
+ currentQueue = queue;
+ queue = [];
+ while (++queueIndex < len) {
+ if (currentQueue) {
+ currentQueue[queueIndex].run();
+ }
+ }
+ queueIndex = -1;
+ len = queue.length;
+ }
+ currentQueue = null;
+ draining = false;
+ clearTimeout(timeout);
+}
+
+process.nextTick = function (fun) {
+ var args = new Array(arguments.length - 1);
+ if (arguments.length > 1) {
+ for (var i = 1; i < arguments.length; i++) {
+ args[i - 1] = arguments[i];
+ }
+ }
+ queue.push(new Item(fun, args));
+ if (queue.length === 1 && !draining) {
+ setTimeout(drainQueue, 0);
+ }
+};
+
+// v8 likes predictible objects
+function Item(fun, array) {
+ this.fun = fun;
+ this.array = array;
+}
+Item.prototype.run = function () {
+ this.fun.apply(null, this.array);
+};
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
+};
+
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+},{}]},{},[19]);
diff --git a/build/vrview-analytics.min.js b/build/vrview-analytics.min.js
new file mode 100644
index 00000000..fab26f84
--- /dev/null
+++ b/build/vrview-analytics.min.js
@@ -0,0 +1,22 @@
+!function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;ot;t+=2){var e=X[t],n=X[t+1];e(n),X[t]=void 0,X[t+1]=void 0}G=0}function f(){try{var t=_dereq_,e=t("vertx");return U=e.runOnLoop||e.runOnContext,i()}catch(n){return c()}}function l(t,e){var n=this,r=n._state;if(r===et&&!t||r===nt&&!e)return this;var o=new this.constructor(p),i=n._result;if(r){var s=arguments[r-1];H(function(){C(r,o,s,i)})}else j(n,o,t,e);return o}function h(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(p);return g(n,t),n}function p(){}function _(){return new TypeError("You cannot resolve a promise with itself")}function v(){return new TypeError("A promises callback cannot return that same promise.")}function d(t){try{return t.then}catch(e){return rt.error=e,rt}}function y(t,e,n,r){try{t.call(e,n,r)}catch(o){return o}}function m(t,e,n){H(function(t){var r=!1,o=y(n,e,function(n){r||(r=!0,e!==n?g(t,n):E(t,n))},function(e){r||(r=!0,S(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&o&&(r=!0,S(t,o))},t)}function w(t,e){e._state===et?E(t,e._result):e._state===nt?S(t,e._result):j(e,void 0,function(e){g(t,e)},function(e){S(t,e)})}function b(t,n,r){n.constructor===t.constructor&&r===Z&&constructor.resolve===$?w(t,n):r===rt?S(t,rt.error):void 0===r?E(t,n):e(r)?m(t,n,r):E(t,n)}function g(e,n){e===n?S(e,_()):t(n)?b(e,n,d(n)):E(e,n)}function A(t){t._onerror&&t._onerror(t._result),T(t)}function E(t,e){t._state===tt&&(t._result=e,t._state=et,0!==t._subscribers.length&&H(T,t))}function S(t,e){t._state===tt&&(t._state=nt,t._result=e,H(A,t))}function j(t,e,n,r){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+et]=n,o[i+nt]=r,0===i&&t._state&&H(T,t)}function T(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,o,i=t._result,s=0;ss;s++)j(r.resolve(t[s]),void 0,e,n);return o}function Y(t){var e=this,n=new e(p);return S(n,t),n}function q(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function F(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function D(t){this._id=ct++,this._state=void 0,this._result=void 0,this._subscribers=[],p!==t&&("function"!=typeof t&&q(),this instanceof D?M(this,t):F())}function K(t,e){this._instanceConstructor=t,this.promise=new t(p),Array.isArray(e)?(this._input=e,this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?E(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&E(this.promise,this._result))):S(this.promise,this._validationError())}function L(){var t;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;(!n||"[object Promise]"!==Object.prototype.toString.call(n.resolve())||n.cast)&&(t.Promise=at)}var N;N=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var U,W,z,B=N,G=0,H=function(t,e){X[G]=t,X[G+1]=e,G+=2,2===G&&(W?W(a):z())},I="undefined"!=typeof window?window:void 0,J=I||{},Q=J.MutationObserver||J.WebKitMutationObserver,R="undefined"!=typeof process&&"[object process]"==={}.toString.call(process),V="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,X=new Array(1e3);z=R?o():Q?s():V?u():void 0===I&&"function"==typeof _dereq_?f():c();var Z=l,$=h,tt=void 0,et=1,nt=2,rt=new P,ot=new P,it=O,st=k,ut=Y,ct=0,at=D;D.all=it,D.race=st,D.resolve=$,D.reject=ut,D._setScheduler=n,D._setAsap=r,D._asap=H,D.prototype={constructor:D,then:Z,"catch":function(t){return this.then(null,t)}};var ft=K;K.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},K.prototype._enumerate=function(){for(var t=this.length,e=this._input,n=0;this._state===tt&&t>n;n++)this._eachEntry(e[n],n)},K.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===$){var o=d(t);if(o===Z&&t._state!==tt)this._settledAt(t._state,e,t._result);else if("function"!=typeof o)this._remaining--,this._result[e]=t;else if(n===at){var i=new n(p);b(i,t,o),this._willSettleAt(i,e)}else this._willSettleAt(new n(function(e){e(t)}),e)}else this._willSettleAt(r(t),e)},K.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===tt&&(this._remaining--,t===nt?S(r,n):this._result[e]=n),0===this._remaining&&E(r,this._result)},K.prototype._willSettleAt=function(t,e){var n=this;j(t,void 0,function(t){n._settledAt(et,e,t)},function(t){n._settledAt(nt,e,t)})};var lt=L,ht={Promise:at,polyfill:lt};"function"==typeof define&&define.amd?define(function(){return ht}):"undefined"!=typeof module&&module.exports?module.exports=ht:"undefined"!=typeof this&&(this.ES6Promise=ht),lt()}).call(this)}).call(this,_dereq_("_process"),"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{_process:20}],2:[function(_dereq_,module,exports){var Stats=function(){var l=Date.now(),m=l,g=0,n=1/0,o=0,h=0,p=1/0,q=0,r=0,s=0,f=document.createElement("div");f.id="stats",f.addEventListener("mousedown",function(b){b.preventDefault(),t(++s%2)},!1),f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps",a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002",f.appendChild(a);var i=document.createElement("div");i.id="fpsText",i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px",i.innerHTML="FPS",a.appendChild(i);var c=document.createElement("div");for(c.id="fpsGraph",c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff",a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113",c.appendChild(j)}var d=document.createElement("div");d.id="ms",d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none",f.appendChild(d);var k=document.createElement("div");k.id="msText",k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px",k.innerHTML="MS",d.appendChild(k);var e=document.createElement("div");for(e.id="msGraph",e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0",d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){switch(s=b){case 0:a.style.display="block",d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:12,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l,n=Math.min(n,g),o=Math.max(o,g),k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));return e.appendChild(e.firstChild).style.height=a+"px",r++,b>m+1e3&&(h=Math.round(1e3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height=a+"px",m=b,r=0),b},update:function(){l=this.end()}}};"object"==typeof module&&(module.exports=Stats)},{}],3:[function(_dereq_,module,exports){var VRControls=function(object,onError){function filterInvalidDevices(devices){var oculusDevices=devices.filter(function(device){return-1!==device.deviceName.toLowerCase().indexOf("oculus")});return oculusDevices.length>=1?devices.filter(function(device){return-1===device.deviceName.toLowerCase().indexOf("cardboard")}):devices}function gotVRDevices(devices){devices=filterInvalidDevices(devices);for(var i=0;ix?-1:x>0?1:+x}),void 0===Function.prototype.name&&void 0!==Object.defineProperty&&Object.defineProperty(Function.prototype,"name",{get:function(){return this.toString().match(/^\s*function\s*(\S*)\s*\(/)[1]}}),THREE.MOUSE={LEFT:0,MIDDLE:1,RIGHT:2},THREE.CullFaceNone=0,THREE.CullFaceBack=1,THREE.CullFaceFront=2,THREE.CullFaceFrontBack=3,THREE.FrontFaceDirectionCW=0,THREE.FrontFaceDirectionCCW=1,THREE.BasicShadowMap=0,THREE.PCFShadowMap=1,THREE.PCFSoftShadowMap=2,THREE.FrontSide=0,THREE.BackSide=1,THREE.DoubleSide=2,THREE.FlatShading=1,THREE.SmoothShading=2,THREE.NoColors=0,THREE.FaceColors=1,THREE.VertexColors=2,THREE.NoBlending=0,THREE.NormalBlending=1,THREE.AdditiveBlending=2,THREE.SubtractiveBlending=3,THREE.MultiplyBlending=4,THREE.CustomBlending=5,THREE.AddEquation=100,THREE.SubtractEquation=101,THREE.ReverseSubtractEquation=102,THREE.MinEquation=103,THREE.MaxEquation=104,THREE.ZeroFactor=200,THREE.OneFactor=201,THREE.SrcColorFactor=202,THREE.OneMinusSrcColorFactor=203,THREE.SrcAlphaFactor=204,THREE.OneMinusSrcAlphaFactor=205,THREE.DstAlphaFactor=206,THREE.OneMinusDstAlphaFactor=207,THREE.DstColorFactor=208,THREE.OneMinusDstColorFactor=209,THREE.SrcAlphaSaturateFactor=210,THREE.NeverDepth=0,THREE.AlwaysDepth=1,THREE.LessDepth=2,THREE.LessEqualDepth=3,THREE.EqualDepth=4,THREE.GreaterEqualDepth=5,THREE.GreaterDepth=6,THREE.NotEqualDepth=7,THREE.MultiplyOperation=0,THREE.MixOperation=1,THREE.AddOperation=2,THREE.UVMapping=300,THREE.CubeReflectionMapping=301,THREE.CubeRefractionMapping=302,THREE.EquirectangularReflectionMapping=303,THREE.EquirectangularRefractionMapping=304,THREE.SphericalReflectionMapping=305,THREE.RepeatWrapping=1e3,THREE.ClampToEdgeWrapping=1001,THREE.MirroredRepeatWrapping=1002,THREE.NearestFilter=1003,THREE.NearestMipMapNearestFilter=1004,THREE.NearestMipMapLinearFilter=1005,THREE.LinearFilter=1006,THREE.LinearMipMapNearestFilter=1007,THREE.LinearMipMapLinearFilter=1008,THREE.UnsignedByteType=1009,THREE.ByteType=1010,THREE.ShortType=1011,THREE.UnsignedShortType=1012,THREE.IntType=1013,THREE.UnsignedIntType=1014,THREE.FloatType=1015,THREE.HalfFloatType=1025,THREE.UnsignedShort4444Type=1016,THREE.UnsignedShort5551Type=1017,THREE.UnsignedShort565Type=1018,THREE.AlphaFormat=1019,THREE.RGBFormat=1020,THREE.RGBAFormat=1021,THREE.LuminanceFormat=1022,THREE.LuminanceAlphaFormat=1023,THREE.RGBEFormat=THREE.RGBAFormat,THREE.RGB_S3TC_DXT1_Format=2001,THREE.RGBA_S3TC_DXT1_Format=2002,THREE.RGBA_S3TC_DXT3_Format=2003,THREE.RGBA_S3TC_DXT5_Format=2004,THREE.RGB_PVRTC_4BPPV1_Format=2100,THREE.RGB_PVRTC_2BPPV1_Format=2101,THREE.RGBA_PVRTC_4BPPV1_Format=2102,THREE.RGBA_PVRTC_2BPPV1_Format=2103,THREE.LoopOnce=2200,THREE.LoopRepeat=2201,THREE.LoopPingPong=2202,THREE.Projector=function(){console.error("THREE.Projector has been moved to /examples/js/renderers/Projector.js."),this.projectVector=function(vector,camera){console.warn("THREE.Projector: .projectVector() is now vector.project()."),vector.project(camera)},this.unprojectVector=function(vector,camera){console.warn("THREE.Projector: .unprojectVector() is now vector.unproject()."),vector.unproject(camera)},this.pickingRay=function(vector,camera){console.error("THREE.Projector: .pickingRay() is now raycaster.setFromCamera().")}},THREE.CanvasRenderer=function(){console.error("THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js"),this.domElement=document.createElement("canvas"),this.clear=function(){},this.render=function(){},this.setClearColor=function(){},this.setSize=function(){}},THREE.Color=function(color){return 3===arguments.length?this.fromArray(arguments):this.set(color)},THREE.Color.prototype={constructor:THREE.Color,r:1,g:1,b:1,set:function(value){return value instanceof THREE.Color?this.copy(value):"number"==typeof value?this.setHex(value):"string"==typeof value&&this.setStyle(value),this},setHex:function(hex){return hex=Math.floor(hex),this.r=(hex>>16&255)/255,this.g=(hex>>8&255)/255,this.b=(255&hex)/255,this},setRGB:function(r,g,b){return this.r=r,this.g=g,this.b=b,this},setHSL:function(){function hue2rgb(p,q,t){return 0>t&&(t+=1),t>1&&(t-=1),1/6>t?p+6*(q-p)*t:.5>t?q:2/3>t?p+6*(q-p)*(2/3-t):p}return function(h,s,l){if(h=THREE.Math.euclideanModulo(h,1),s=THREE.Math.clamp(s,0,1),l=THREE.Math.clamp(l,0,1),0===s)this.r=this.g=this.b=l;else{var p=.5>=l?l*(1+s):l+s-l*s,q=2*l-p;this.r=hue2rgb(q,p,h+1/3),this.g=hue2rgb(q,p,h),this.b=hue2rgb(q,p,h-1/3)}return this}}(),setStyle:function(style){function handleAlpha(string){void 0!==string&&parseFloat(string)<1&&console.warn("THREE.Color: Alpha component of "+style+" will be ignored.")}var m;if(m=/^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec(style)){var color,name=m[1],components=m[2];switch(name){case"rgb":case"rgba":if(color=/^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(components))return this.r=Math.min(255,parseInt(color[1],10))/255,this.g=Math.min(255,parseInt(color[2],10))/255,this.b=Math.min(255,parseInt(color[3],10))/255,handleAlpha(color[5]),this;if(color=/^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(components))return this.r=Math.min(100,parseInt(color[1],10))/100,this.g=Math.min(100,parseInt(color[2],10))/100,this.b=Math.min(100,parseInt(color[3],10))/100,handleAlpha(color[5]),this;break;case"hsl":case"hsla":if(color=/^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(components)){var h=parseFloat(color[1])/360,s=parseInt(color[2],10)/100,l=parseInt(color[3],10)/100;return handleAlpha(color[5]),this.setHSL(h,s,l)}}}else if(m=/^\#([A-Fa-f0-9]+)$/.exec(style)){var hex=m[1],size=hex.length;if(3===size)return this.r=parseInt(hex.charAt(0)+hex.charAt(0),16)/255,this.g=parseInt(hex.charAt(1)+hex.charAt(1),16)/255,this.b=parseInt(hex.charAt(2)+hex.charAt(2),16)/255,this;if(6===size)return this.r=parseInt(hex.charAt(0)+hex.charAt(1),16)/255,this.g=parseInt(hex.charAt(2)+hex.charAt(3),16)/255,this.b=parseInt(hex.charAt(4)+hex.charAt(5),16)/255,this}if(style&&style.length>0){var hex=THREE.ColorKeywords[style];void 0!==hex?this.setHex(hex):console.warn("THREE.Color: Unknown color "+style)}return this},clone:function(){return new this.constructor(this.r,this.g,this.b)},copy:function(color){return this.r=color.r,this.g=color.g,this.b=color.b,this},copyGammaToLinear:function(color,gammaFactor){return void 0===gammaFactor&&(gammaFactor=2),this.r=Math.pow(color.r,gammaFactor),this.g=Math.pow(color.g,gammaFactor),this.b=Math.pow(color.b,gammaFactor),this},copyLinearToGamma:function(color,gammaFactor){void 0===gammaFactor&&(gammaFactor=2);var safeInverse=gammaFactor>0?1/gammaFactor:1;return this.r=Math.pow(color.r,safeInverse),this.g=Math.pow(color.g,safeInverse),this.b=Math.pow(color.b,safeInverse),this},convertGammaToLinear:function(){var r=this.r,g=this.g,b=this.b;return this.r=r*r,this.g=g*g,this.b=b*b,this},convertLinearToGamma:function(){return this.r=Math.sqrt(this.r),this.g=Math.sqrt(this.g),this.b=Math.sqrt(this.b),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(optionalTarget){var hue,saturation,hsl=optionalTarget||{h:0,s:0,l:0},r=this.r,g=this.g,b=this.b,max=Math.max(r,g,b),min=Math.min(r,g,b),lightness=(min+max)/2;if(min===max)hue=0,saturation=0;else{var delta=max-min;switch(saturation=.5>=lightness?delta/(max+min):delta/(2-max-min),max){case r:hue=(g-b)/delta+(b>g?6:0);break;case g:hue=(b-r)/delta+2;break;case b:hue=(r-g)/delta+4}hue/=6}return hsl.h=hue,hsl.s=saturation,hsl.l=lightness,hsl},getStyle:function(){return"rgb("+(255*this.r|0)+","+(255*this.g|0)+","+(255*this.b|0)+")"},offsetHSL:function(h,s,l){var hsl=this.getHSL();return hsl.h+=h,hsl.s+=s,hsl.l+=l,this.setHSL(hsl.h,hsl.s,hsl.l),this},add:function(color){return this.r+=color.r,this.g+=color.g,this.b+=color.b,this},addColors:function(color1,color2){return this.r=color1.r+color2.r,this.g=color1.g+color2.g,this.b=color1.b+color2.b,this},addScalar:function(s){return this.r+=s,this.g+=s,this.b+=s,this},multiply:function(color){return this.r*=color.r,this.g*=color.g,this.b*=color.b,this},multiplyScalar:function(s){return this.r*=s,this.g*=s,this.b*=s,this},lerp:function(color,alpha){return this.r+=(color.r-this.r)*alpha,this.g+=(color.g-this.g)*alpha,this.b+=(color.b-this.b)*alpha,this},equals:function(c){return c.r===this.r&&c.g===this.g&&c.b===this.b},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.r=array[offset],this.g=array[offset+1],this.b=array[offset+2],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.r,array[offset+1]=this.g,array[offset+2]=this.b,array}},THREE.ColorKeywords={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},THREE.Quaternion=function(x,y,z,w){this._x=x||0,this._y=y||0,this._z=z||0,this._w=void 0!==w?w:1},THREE.Quaternion.prototype={constructor:THREE.Quaternion,get x(){return this._x},set x(value){this._x=value,this.onChangeCallback()},get y(){return this._y},set y(value){this._y=value,this.onChangeCallback()},get z(){return this._z},set z(value){this._z=value,this.onChangeCallback()},get w(){return this._w},set w(value){this._w=value,this.onChangeCallback()},set:function(x,y,z,w){return this._x=x,this._y=y,this._z=z,this._w=w,this.onChangeCallback(),this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._w)},copy:function(quaternion){return this._x=quaternion.x,this._y=quaternion.y,this._z=quaternion.z,this._w=quaternion.w,this.onChangeCallback(),this},setFromEuler:function(euler,update){if(euler instanceof THREE.Euler==!1)throw new Error("THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var c1=Math.cos(euler._x/2),c2=Math.cos(euler._y/2),c3=Math.cos(euler._z/2),s1=Math.sin(euler._x/2),s2=Math.sin(euler._y/2),s3=Math.sin(euler._z/2),order=euler.order;return"XYZ"===order?(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):"YXZ"===order?(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):"ZXY"===order?(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):"ZYX"===order?(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):"YZX"===order?(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):"XZY"===order&&(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),update!==!1&&this.onChangeCallback(),this},setFromAxisAngle:function(axis,angle){var halfAngle=angle/2,s=Math.sin(halfAngle);return this._x=axis.x*s,this._y=axis.y*s,this._z=axis.z*s,this._w=Math.cos(halfAngle),this.onChangeCallback(),this},setFromRotationMatrix:function(m){var s,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10],trace=m11+m22+m33;return trace>0?(s=.5/Math.sqrt(trace+1),this._w=.25/s,this._x=(m32-m23)*s,this._y=(m13-m31)*s,this._z=(m21-m12)*s):m11>m22&&m11>m33?(s=2*Math.sqrt(1+m11-m22-m33),this._w=(m32-m23)/s,this._x=.25*s,this._y=(m12+m21)/s,this._z=(m13+m31)/s):m22>m33?(s=2*Math.sqrt(1+m22-m11-m33),this._w=(m13-m31)/s,this._x=(m12+m21)/s,this._y=.25*s,this._z=(m23+m32)/s):(s=2*Math.sqrt(1+m33-m11-m22),this._w=(m21-m12)/s,this._x=(m13+m31)/s,this._y=(m23+m32)/s,this._z=.25*s),this.onChangeCallback(),this},setFromUnitVectors:function(){var v1,r,EPS=1e-6;return function(vFrom,vTo){return void 0===v1&&(v1=new THREE.Vector3),r=vFrom.dot(vTo)+1,EPS>r?(r=0,Math.abs(vFrom.x)>Math.abs(vFrom.z)?v1.set(-vFrom.y,vFrom.x,0):v1.set(0,-vFrom.z,vFrom.y)):v1.crossVectors(vFrom,vTo),this._x=v1.x,this._y=v1.y,this._z=v1.z,this._w=r,this.normalize(),this}}(),inverse:function(){return this.conjugate().normalize(),this},conjugate:function(){return this._x*=-1,this._y*=-1,this._z*=-1,this.onChangeCallback(),this},dot:function(v){return this._x*v._x+this._y*v._y+this._z*v._z+this._w*v._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var l=this.length();return 0===l?(this._x=0,this._y=0,this._z=0,this._w=1):(l=1/l,this._x=this._x*l,this._y=this._y*l,this._z=this._z*l,this._w=this._w*l),this.onChangeCallback(),this},multiply:function(q,p){return void 0!==p?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(q,p)):this.multiplyQuaternions(this,q)},multiplyQuaternions:function(a,b){var qax=a._x,qay=a._y,qaz=a._z,qaw=a._w,qbx=b._x,qby=b._y,qbz=b._z,qbw=b._w;return this._x=qax*qbw+qaw*qbx+qay*qbz-qaz*qby,this._y=qay*qbw+qaw*qby+qaz*qbx-qax*qbz,this._z=qaz*qbw+qaw*qbz+qax*qby-qay*qbx,this._w=qaw*qbw-qax*qbx-qay*qby-qaz*qbz,this.onChangeCallback(),this},multiplyVector3:function(vector){return console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."),vector.applyQuaternion(this)},slerp:function(qb,t){if(0===t)return this;if(1===t)return this.copy(qb);var x=this._x,y=this._y,z=this._z,w=this._w,cosHalfTheta=w*qb._w+x*qb._x+y*qb._y+z*qb._z;if(0>cosHalfTheta?(this._w=-qb._w,this._x=-qb._x,this._y=-qb._y,this._z=-qb._z,cosHalfTheta=-cosHalfTheta):this.copy(qb),cosHalfTheta>=1)return this._w=w,this._x=x,this._y=y,this._z=z,this;var halfTheta=Math.acos(cosHalfTheta),sinHalfTheta=Math.sqrt(1-cosHalfTheta*cosHalfTheta);
+if(Math.abs(sinHalfTheta)<.001)return this._w=.5*(w+this._w),this._x=.5*(x+this._x),this._y=.5*(y+this._y),this._z=.5*(z+this._z),this;var ratioA=Math.sin((1-t)*halfTheta)/sinHalfTheta,ratioB=Math.sin(t*halfTheta)/sinHalfTheta;return this._w=w*ratioA+this._w*ratioB,this._x=x*ratioA+this._x*ratioB,this._y=y*ratioA+this._y*ratioB,this._z=z*ratioA+this._z*ratioB,this.onChangeCallback(),this},equals:function(quaternion){return quaternion._x===this._x&&quaternion._y===this._y&&quaternion._z===this._z&&quaternion._w===this._w},fromArray:function(array,offset){return void 0===offset&&(offset=0),this._x=array[offset],this._y=array[offset+1],this._z=array[offset+2],this._w=array[offset+3],this.onChangeCallback(),this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this._x,array[offset+1]=this._y,array[offset+2]=this._z,array[offset+3]=this._w,array},onChange:function(callback){return this.onChangeCallback=callback,this},onChangeCallback:function(){}},THREE.Quaternion.slerp=function(qa,qb,qm,t){return qm.copy(qa).slerp(qb,t)},THREE.Vector2=function(x,y){this.x=x||0,this.y=y||0},THREE.Vector2.prototype={constructor:THREE.Vector2,get width(){return this.x},set width(value){this.x=value},get height(){return this.y},set height(value){this.y=value},set:function(x,y){return this.x=x,this.y=y,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+index)}},clone:function(){return new this.constructor(this.x,this.y)},copy:function(v){return this.x=v.x,this.y=v.y,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this)},addScalar:function(s){return this.x+=s,this.y+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this},addScaledVector:function(v,s){return this.x+=v.x*s,this.y+=v.y*s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this)},subScalar:function(s){return this.x-=s,this.y-=s,this},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this},multiply:function(v){return this.x*=v.x,this.y*=v.y,this},multiplyScalar:function(scalar){return isFinite(scalar)?(this.x*=scalar,this.y*=scalar):(this.x=0,this.y=0),this},divide:function(v){return this.x/=v.x,this.y/=v.y,this},divideScalar:function(scalar){return this.multiplyScalar(1/scalar)},min:function(v){return this.x=Math.min(this.x,v.x),this.y=Math.min(this.y,v.y),this},max:function(v){return this.x=Math.max(this.x,v.x),this.y=Math.max(this.y,v.y),this},clamp:function(min,max){return this.x=Math.max(min.x,Math.min(max.x,this.x)),this.y=Math.max(min.y,Math.min(max.y,this.y)),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector2,max=new THREE.Vector2),min.set(minVal,minVal),max.set(maxVal,maxVal),this.clamp(min,max)}}(),clampLength:function(min,max){var length=this.length();return this.multiplyScalar(Math.max(min,Math.min(max,length))/length),this},floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this},negate:function(){return this.x=-this.x,this.y=-this.y,this},dot:function(v){return this.x*v.x+this.y*v.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)},normalize:function(){return this.divideScalar(this.length())},distanceTo:function(v){return Math.sqrt(this.distanceToSquared(v))},distanceToSquared:function(v){var dx=this.x-v.x,dy=this.y-v.y;return dx*dx+dy*dy},setLength:function(length){return this.multiplyScalar(length/this.length())},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this},lerpVectors:function(v1,v2,alpha){return this.subVectors(v2,v1).multiplyScalar(alpha).add(v1),this},equals:function(v){return v.x===this.x&&v.y===this.y},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this},rotateAround:function(center,angle){var c=Math.cos(angle),s=Math.sin(angle),x=this.x-center.x,y=this.y-center.y;return this.x=x*c-y*s+center.x,this.y=x*s+y*c+center.y,this}},THREE.Vector3=function(x,y,z){this.x=x||0,this.y=y||0,this.z=z||0},THREE.Vector3.prototype={constructor:THREE.Vector3,set:function(x,y,z){return this.x=x,this.y=y,this.z=z,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setZ:function(z){return this.z=z,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;case 2:this.z=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+index)}},clone:function(){return new this.constructor(this.x,this.y,this.z)},copy:function(v){return this.x=v.x,this.y=v.y,this.z=v.z,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this.z+=v.z,this)},addScalar:function(s){return this.x+=s,this.y+=s,this.z+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this.z=a.z+b.z,this},addScaledVector:function(v,s){return this.x+=v.x*s,this.y+=v.y*s,this.z+=v.z*s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this.z-=v.z,this)},subScalar:function(s){return this.x-=s,this.y-=s,this.z-=s,this},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this.z=a.z-b.z,this},multiply:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(v,w)):(this.x*=v.x,this.y*=v.y,this.z*=v.z,this)},multiplyScalar:function(scalar){return isFinite(scalar)?(this.x*=scalar,this.y*=scalar,this.z*=scalar):(this.x=0,this.y=0,this.z=0),this},multiplyVectors:function(a,b){return this.x=a.x*b.x,this.y=a.y*b.y,this.z=a.z*b.z,this},applyEuler:function(){var quaternion;return function(euler){return euler instanceof THREE.Euler==!1&&console.error("THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order."),void 0===quaternion&&(quaternion=new THREE.Quaternion),this.applyQuaternion(quaternion.setFromEuler(euler)),this}}(),applyAxisAngle:function(){var quaternion;return function(axis,angle){return void 0===quaternion&&(quaternion=new THREE.Quaternion),this.applyQuaternion(quaternion.setFromAxisAngle(axis,angle)),this}}(),applyMatrix3:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[3]*y+e[6]*z,this.y=e[1]*x+e[4]*y+e[7]*z,this.z=e[2]*x+e[5]*y+e[8]*z,this},applyMatrix4:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z+e[12],this.y=e[1]*x+e[5]*y+e[9]*z+e[13],this.z=e[2]*x+e[6]*y+e[10]*z+e[14],this},applyProjection:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements,d=1/(e[3]*x+e[7]*y+e[11]*z+e[15]);return this.x=(e[0]*x+e[4]*y+e[8]*z+e[12])*d,this.y=(e[1]*x+e[5]*y+e[9]*z+e[13])*d,this.z=(e[2]*x+e[6]*y+e[10]*z+e[14])*d,this},applyQuaternion:function(q){var x=this.x,y=this.y,z=this.z,qx=q.x,qy=q.y,qz=q.z,qw=q.w,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;return this.x=ix*qw+iw*-qx+iy*-qz-iz*-qy,this.y=iy*qw+iw*-qy+iz*-qx-ix*-qz,this.z=iz*qw+iw*-qz+ix*-qy-iy*-qx,this},project:function(){var matrix;return function(camera){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.multiplyMatrices(camera.projectionMatrix,matrix.getInverse(camera.matrixWorld)),this.applyProjection(matrix)}}(),unproject:function(){var matrix;return function(camera){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.multiplyMatrices(camera.matrixWorld,matrix.getInverse(camera.projectionMatrix)),this.applyProjection(matrix)}}(),transformDirection:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z,this.y=e[1]*x+e[5]*y+e[9]*z,this.z=e[2]*x+e[6]*y+e[10]*z,this.normalize(),this},divide:function(v){return this.x/=v.x,this.y/=v.y,this.z/=v.z,this},divideScalar:function(scalar){return this.multiplyScalar(1/scalar)},min:function(v){return this.x=Math.min(this.x,v.x),this.y=Math.min(this.y,v.y),this.z=Math.min(this.z,v.z),this},max:function(v){return this.x=Math.max(this.x,v.x),this.y=Math.max(this.y,v.y),this.z=Math.max(this.z,v.z),this},clamp:function(min,max){return this.x=Math.max(min.x,Math.min(max.x,this.x)),this.y=Math.max(min.y,Math.min(max.y,this.y)),this.z=Math.max(min.z,Math.min(max.z,this.z)),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector3,max=new THREE.Vector3),min.set(minVal,minVal,minVal),max.set(maxVal,maxVal,maxVal),this.clamp(min,max)}}(),clampLength:function(min,max){var length=this.length();return this.multiplyScalar(Math.max(min,Math.min(max,length))/length),this},floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this},dot:function(v){return this.x*v.x+this.y*v.y+this.z*v.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)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length())},setLength:function(length){return this.multiplyScalar(length/this.length())},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this.z+=(v.z-this.z)*alpha,this},lerpVectors:function(v1,v2,alpha){return this.subVectors(v2,v1).multiplyScalar(alpha).add(v1),this},cross:function(v,w){if(void 0!==w)return console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(v,w);var x=this.x,y=this.y,z=this.z;return this.x=y*v.z-z*v.y,this.y=z*v.x-x*v.z,this.z=x*v.y-y*v.x,this},crossVectors:function(a,b){var ax=a.x,ay=a.y,az=a.z,bx=b.x,by=b.y,bz=b.z;return this.x=ay*bz-az*by,this.y=az*bx-ax*bz,this.z=ax*by-ay*bx,this},projectOnVector:function(){var v1,dot;return function(vector){return void 0===v1&&(v1=new THREE.Vector3),v1.copy(vector).normalize(),dot=this.dot(v1),this.copy(v1).multiplyScalar(dot)}}(),projectOnPlane:function(){var v1;return function(planeNormal){return void 0===v1&&(v1=new THREE.Vector3),v1.copy(this).projectOnVector(planeNormal),this.sub(v1)}}(),reflect:function(){var v1;return function(normal){return void 0===v1&&(v1=new THREE.Vector3),this.sub(v1.copy(normal).multiplyScalar(2*this.dot(normal)))}}(),angleTo:function(v){var theta=this.dot(v)/(this.length()*v.length());return Math.acos(THREE.Math.clamp(theta,-1,1))},distanceTo:function(v){return Math.sqrt(this.distanceToSquared(v))},distanceToSquared:function(v){var dx=this.x-v.x,dy=this.y-v.y,dz=this.z-v.z;return dx*dx+dy*dy+dz*dz},setEulerFromRotationMatrix:function(m,order){console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.")},setEulerFromQuaternion:function(q,order){console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.")},getPositionFromMatrix:function(m){return console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."),this.setFromMatrixPosition(m)},getScaleFromMatrix:function(m){return console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."),this.setFromMatrixScale(m)},getColumnFromMatrix:function(index,matrix){return console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."),this.setFromMatrixColumn(index,matrix)},setFromMatrixPosition:function(m){return this.x=m.elements[12],this.y=m.elements[13],this.z=m.elements[14],this},setFromMatrixScale:function(m){var sx=this.set(m.elements[0],m.elements[1],m.elements[2]).length(),sy=this.set(m.elements[4],m.elements[5],m.elements[6]).length(),sz=this.set(m.elements[8],m.elements[9],m.elements[10]).length();return this.x=sx,this.y=sy,this.z=sz,this},setFromMatrixColumn:function(index,matrix){var offset=4*index,me=matrix.elements;return this.x=me[offset],this.y=me[offset+1],this.z=me[offset+2],this},equals:function(v){return v.x===this.x&&v.y===this.y&&v.z===this.z},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this.z=array[offset+2],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array[offset+2]=this.z,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this.z=attribute.array[index+2],this}},THREE.Vector4=function(x,y,z,w){this.x=x||0,this.y=y||0,this.z=z||0,this.w=void 0!==w?w:1},THREE.Vector4.prototype={constructor:THREE.Vector4,set:function(x,y,z,w){return this.x=x,this.y=y,this.z=z,this.w=w,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setZ:function(z){return this.z=z,this},setW:function(w){return this.w=w,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;case 2:this.z=value;break;case 3:this.w=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("index is out of range: "+index)}},clone:function(){return new this.constructor(this.x,this.y,this.z,this.w)},copy:function(v){return this.x=v.x,this.y=v.y,this.z=v.z,this.w=void 0!==v.w?v.w:1,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this.z+=v.z,this.w+=v.w,this)},addScalar:function(s){return this.x+=s,this.y+=s,this.z+=s,this.w+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this.z=a.z+b.z,this.w=a.w+b.w,this},addScaledVector:function(v,s){return this.x+=v.x*s,this.y+=v.y*s,this.z+=v.z*s,this.w+=v.w*s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this.z-=v.z,this.w-=v.w,this)},subScalar:function(s){return this.x-=s,this.y-=s,this.z-=s,this.w-=s,this},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this.z=a.z-b.z,this.w=a.w-b.w,this},multiplyScalar:function(scalar){return isFinite(scalar)?(this.x*=scalar,this.y*=scalar,this.z*=scalar,this.w*=scalar):(this.x=0,this.y=0,this.z=0,this.w=0),this},applyMatrix4:function(m){var x=this.x,y=this.y,z=this.z,w=this.w,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z+e[12]*w,this.y=e[1]*x+e[5]*y+e[9]*z+e[13]*w,this.z=e[2]*x+e[6]*y+e[10]*z+e[14]*w,this.w=e[3]*x+e[7]*y+e[11]*z+e[15]*w,this},divideScalar:function(scalar){return this.multiplyScalar(1/scalar)},setAxisAngleFromQuaternion:function(q){this.w=2*Math.acos(q.w);var s=Math.sqrt(1-q.w*q.w);return 1e-4>s?(this.x=1,this.y=0,this.z=0):(this.x=q.x/s,this.y=q.y/s,this.z=q.z/s),this},setAxisAngleFromRotationMatrix:function(m){var angle,x,y,z,epsilon=.01,epsilon2=.1,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10];if(Math.abs(m12-m21)yy&&xx>zz?epsilon>xx?(x=0,y=.707106781,z=.707106781):(x=Math.sqrt(xx),y=xy/x,z=xz/x):yy>zz?epsilon>yy?(x=.707106781,y=0,z=.707106781):(y=Math.sqrt(yy),x=xy/y,z=yz/y):epsilon>zz?(x=.707106781,y=.707106781,z=0):(z=Math.sqrt(zz),x=xz/z,y=yz/z),this.set(x,y,z,angle),this}var s=Math.sqrt((m32-m23)*(m32-m23)+(m13-m31)*(m13-m31)+(m21-m12)*(m21-m12));return Math.abs(s)<.001&&(s=1),this.x=(m32-m23)/s,this.y=(m13-m31)/s,this.z=(m21-m12)/s,this.w=Math.acos((m11+m22+m33-1)/2),this},min:function(v){return this.x=Math.min(this.x,v.x),this.y=Math.min(this.y,v.y),this.z=Math.min(this.z,v.z),this.w=Math.min(this.w,v.w),this},max:function(v){return this.x=Math.max(this.x,v.x),this.y=Math.max(this.y,v.y),this.z=Math.max(this.z,v.z),this.w=Math.max(this.w,v.w),this},clamp:function(min,max){return this.x=Math.max(min.x,Math.min(max.x,this.x)),this.y=Math.max(min.y,Math.min(max.y,this.y)),this.z=Math.max(min.z,Math.min(max.z,this.z)),this.w=Math.max(min.w,Math.min(max.w,this.w)),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector4,max=new THREE.Vector4),min.set(minVal,minVal,minVal,minVal),max.set(maxVal,maxVal,maxVal,maxVal),this.clamp(min,max)}}(),floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this.w=Math.floor(this.w),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this.w=Math.ceil(this.w),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this.w=Math.round(this.w),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this.w=this.w<0?Math.ceil(this.w):Math.floor(this.w),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this.w=-this.w,this},dot:function(v){return this.x*v.x+this.y*v.y+this.z*v.z+this.w*v.w},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},lengthManhattan: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())},setLength:function(length){return this.multiplyScalar(length/this.length())},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this.z+=(v.z-this.z)*alpha,this.w+=(v.w-this.w)*alpha,this},lerpVectors:function(v1,v2,alpha){return this.subVectors(v2,v1).multiplyScalar(alpha).add(v1),this},equals:function(v){return v.x===this.x&&v.y===this.y&&v.z===this.z&&v.w===this.w},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this.z=array[offset+2],this.w=array[offset+3],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array[offset+2]=this.z,array[offset+3]=this.w,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this.z=attribute.array[index+2],this.w=attribute.array[index+3],this}},THREE.Euler=function(x,y,z,order){this._x=x||0,this._y=y||0,this._z=z||0,this._order=order||THREE.Euler.DefaultOrder},THREE.Euler.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"],THREE.Euler.DefaultOrder="XYZ",THREE.Euler.prototype={constructor:THREE.Euler,get x(){return this._x},set x(value){this._x=value,this.onChangeCallback()},get y(){return this._y},set y(value){this._y=value,this.onChangeCallback()},get z(){return this._z},set z(value){this._z=value,this.onChangeCallback()},get order(){return this._order},set order(value){this._order=value,this.onChangeCallback()},set:function(x,y,z,order){return this._x=x,this._y=y,this._z=z,this._order=order||this._order,this.onChangeCallback(),this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._order)},copy:function(euler){return this._x=euler._x,this._y=euler._y,this._z=euler._z,this._order=euler._order,this.onChangeCallback(),this},setFromRotationMatrix:function(m,order,update){var clamp=THREE.Math.clamp,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10];return order=order||this._order,"XYZ"===order?(this._y=Math.asin(clamp(m13,-1,1)),Math.abs(m13)<.99999?(this._x=Math.atan2(-m23,m33),this._z=Math.atan2(-m12,m11)):(this._x=Math.atan2(m32,m22),this._z=0)):"YXZ"===order?(this._x=Math.asin(-clamp(m23,-1,1)),Math.abs(m23)<.99999?(this._y=Math.atan2(m13,m33),this._z=Math.atan2(m21,m22)):(this._y=Math.atan2(-m31,m11),this._z=0)):"ZXY"===order?(this._x=Math.asin(clamp(m32,-1,1)),Math.abs(m32)<.99999?(this._y=Math.atan2(-m31,m33),this._z=Math.atan2(-m12,m22)):(this._y=0,this._z=Math.atan2(m21,m11))):"ZYX"===order?(this._y=Math.asin(-clamp(m31,-1,1)),Math.abs(m31)<.99999?(this._x=Math.atan2(m32,m33),this._z=Math.atan2(m21,m11)):(this._x=0,this._z=Math.atan2(-m12,m22))):"YZX"===order?(this._z=Math.asin(clamp(m21,-1,1)),Math.abs(m21)<.99999?(this._x=Math.atan2(-m23,m22),this._y=Math.atan2(-m31,m11)):(this._x=0,this._y=Math.atan2(m13,m33))):"XZY"===order?(this._z=Math.asin(-clamp(m12,-1,1)),Math.abs(m12)<.99999?(this._x=Math.atan2(m32,m22),this._y=Math.atan2(m13,m11)):(this._x=Math.atan2(-m23,m33),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+order),this._order=order,update!==!1&&this.onChangeCallback(),this},setFromQuaternion:function(){var matrix;return function(q,order,update){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.makeRotationFromQuaternion(q),this.setFromRotationMatrix(matrix,order,update),this}}(),setFromVector3:function(v,order){return this.set(v.x,v.y,v.z,order||this._order)},reorder:function(){var q=new THREE.Quaternion;return function(newOrder){q.setFromEuler(this),this.setFromQuaternion(q,newOrder)}}(),equals:function(euler){return euler._x===this._x&&euler._y===this._y&&euler._z===this._z&&euler._order===this._order},fromArray:function(array){return this._x=array[0],this._y=array[1],this._z=array[2],void 0!==array[3]&&(this._order=array[3]),this.onChangeCallback(),this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this._x,array[offset+1]=this._y,array[offset+2]=this._z,array[offset+3]=this._order,array},toVector3:function(optionalResult){return optionalResult?optionalResult.set(this._x,this._y,this._z):new THREE.Vector3(this._x,this._y,this._z)},onChange:function(callback){return this.onChangeCallback=callback,this},onChangeCallback:function(){}},THREE.Line3=function(start,end){this.start=void 0!==start?start:new THREE.Vector3,this.end=void 0!==end?end:new THREE.Vector3},THREE.Line3.prototype={constructor:THREE.Line3,set:function(start,end){return this.start.copy(start),this.end.copy(end),this},clone:function(){return(new this.constructor).copy(this)},copy:function(line){return this.start.copy(line.start),this.end.copy(line.end),this},center:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.addVectors(this.start,this.end).multiplyScalar(.5)},delta:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(t,optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.delta(result).multiplyScalar(t).add(this.start)},closestPointToPointParameter:function(){var startP=new THREE.Vector3,startEnd=new THREE.Vector3;return function(point,clampToLine){startP.subVectors(point,this.start),startEnd.subVectors(this.end,this.start);var startEnd2=startEnd.dot(startEnd),startEnd_startP=startEnd.dot(startP),t=startEnd_startP/startEnd2;return clampToLine&&(t=THREE.Math.clamp(t,0,1)),t}}(),closestPointToPoint:function(point,clampToLine,optionalTarget){var t=this.closestPointToPointParameter(point,clampToLine),result=optionalTarget||new THREE.Vector3;return this.delta(result).multiplyScalar(t).add(this.start)},applyMatrix4:function(matrix){return this.start.applyMatrix4(matrix),this.end.applyMatrix4(matrix),this},equals:function(line){return line.start.equals(this.start)&&line.end.equals(this.end)}},THREE.Box2=function(min,max){this.min=void 0!==min?min:new THREE.Vector2(1/0,1/0),this.max=void 0!==max?max:new THREE.Vector2(-(1/0),-(1/0))},THREE.Box2.prototype={constructor:THREE.Box2,set:function(min,max){return this.min.copy(min),this.max.copy(max),this},setFromPoints:function(points){this.makeEmpty();for(var i=0,il=points.length;il>i;i++)this.expandByPoint(points[i]);return this},setFromCenterAndSize:function(){var v1=new THREE.Vector2;return function(center,size){var halfSize=v1.copy(size).multiplyScalar(.5);return this.min.copy(center).sub(halfSize),this.max.copy(center).add(halfSize),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(box){return this.min.copy(box.min),this.max.copy(box.max),this},makeEmpty:function(){return this.min.x=this.min.y=1/0,this.max.x=this.max.y=-(1/0),this},empty:function(){return this.max.xthis.max.x||point.ythis.max.y)},containsBox:function(box){return this.min.x<=box.min.x&&box.max.x<=this.max.x&&this.min.y<=box.min.y&&box.max.y<=this.max.y},getParameter:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector2;return result.set((point.x-this.min.x)/(this.max.x-this.min.x),(point.y-this.min.y)/(this.max.y-this.min.y))},isIntersectionBox:function(box){return!(box.max.xthis.max.x||box.max.ythis.max.y)},clampPoint:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector2;return result.copy(point).clamp(this.min,this.max)},distanceToPoint:function(){var v1=new THREE.Vector2;return function(point){var clampedPoint=v1.copy(point).clamp(this.min,this.max);return clampedPoint.sub(point).length()}}(),intersect:function(box){return this.min.max(box.min),this.max.min(box.max),this},union:function(box){return this.min.min(box.min),this.max.max(box.max),this},translate:function(offset){return this.min.add(offset),this.max.add(offset),this},equals:function(box){return box.min.equals(this.min)&&box.max.equals(this.max)}},THREE.Box3=function(min,max){this.min=void 0!==min?min:new THREE.Vector3(1/0,1/0,1/0),this.max=void 0!==max?max:new THREE.Vector3(-(1/0),-(1/0),-(1/0))},THREE.Box3.prototype={constructor:THREE.Box3,set:function(min,max){return this.min.copy(min),this.max.copy(max),this},setFromPoints:function(points){this.makeEmpty();for(var i=0,il=points.length;il>i;i++)this.expandByPoint(points[i]);return this},setFromCenterAndSize:function(){var v1=new THREE.Vector3;return function(center,size){var halfSize=v1.copy(size).multiplyScalar(.5);return this.min.copy(center).sub(halfSize),this.max.copy(center).add(halfSize),this}}(),setFromObject:function(){var v1=new THREE.Vector3;return function(object){var scope=this;return object.updateMatrixWorld(!0),this.makeEmpty(),object.traverse(function(node){var geometry=node.geometry;if(void 0!==geometry)if(geometry instanceof THREE.Geometry)for(var vertices=geometry.vertices,i=0,il=vertices.length;il>i;i++)v1.copy(vertices[i]),v1.applyMatrix4(node.matrixWorld),scope.expandByPoint(v1);else if(geometry instanceof THREE.BufferGeometry&&void 0!==geometry.attributes.position)for(var positions=geometry.attributes.position.array,i=0,il=positions.length;il>i;i+=3)v1.set(positions[i],positions[i+1],positions[i+2]),v1.applyMatrix4(node.matrixWorld),scope.expandByPoint(v1)}),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(box){return this.min.copy(box.min),this.max.copy(box.max),this},makeEmpty:function(){return this.min.x=this.min.y=this.min.z=1/0,this.max.x=this.max.y=this.max.z=-(1/0),this},empty:function(){return this.max.xthis.max.x||point.ythis.max.y||point.zthis.max.z)},containsBox:function(box){return this.min.x<=box.min.x&&box.max.x<=this.max.x&&this.min.y<=box.min.y&&box.max.y<=this.max.y&&this.min.z<=box.min.z&&box.max.z<=this.max.z},getParameter:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.set((point.x-this.min.x)/(this.max.x-this.min.x),(point.y-this.min.y)/(this.max.y-this.min.y),(point.z-this.min.z)/(this.max.z-this.min.z))},isIntersectionBox:function(box){return!(box.max.xthis.max.x||box.max.ythis.max.y||box.max.zthis.max.z)},clampPoint:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.copy(point).clamp(this.min,this.max)},distanceToPoint:function(){var v1=new THREE.Vector3;return function(point){
+var clampedPoint=v1.copy(point).clamp(this.min,this.max);return clampedPoint.sub(point).length()}}(),getBoundingSphere:function(){var v1=new THREE.Vector3;return function(optionalTarget){var result=optionalTarget||new THREE.Sphere;return result.center=this.center(),result.radius=.5*this.size(v1).length(),result}}(),intersect:function(box){return this.min.max(box.min),this.max.min(box.max),this},union:function(box){return this.min.min(box.min),this.max.max(box.max),this},applyMatrix4:function(){var points=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];return function(matrix){return points[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(matrix),points[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(matrix),points[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(matrix),points[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(matrix),points[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(matrix),points[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(matrix),points[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(matrix),points[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(matrix),this.makeEmpty(),this.setFromPoints(points),this}}(),translate:function(offset){return this.min.add(offset),this.max.add(offset),this},equals:function(box){return box.min.equals(this.min)&&box.max.equals(this.max)}},THREE.Matrix3=function(){this.elements=new Float32Array([1,0,0,0,1,0,0,0,1]),arguments.length>0&&console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.")},THREE.Matrix3.prototype={constructor:THREE.Matrix3,set:function(n11,n12,n13,n21,n22,n23,n31,n32,n33){var te=this.elements;return te[0]=n11,te[3]=n12,te[6]=n13,te[1]=n21,te[4]=n22,te[7]=n23,te[2]=n31,te[5]=n32,te[8]=n33,this},identity:function(){return this.set(1,0,0,0,1,0,0,0,1),this},clone:function(){return(new this.constructor).fromArray(this.elements)},copy:function(m){var me=m.elements;return this.set(me[0],me[3],me[6],me[1],me[4],me[7],me[2],me[5],me[8]),this},multiplyVector3:function(vector){return console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead."),vector.applyMatrix3(this)},multiplyVector3Array:function(a){return console.warn("THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead."),this.applyToVector3Array(a)},applyToVector3Array:function(){var v1;return function(array,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=array.length);for(var i=0,j=offset;length>i;i+=3,j+=3)v1.fromArray(array,j),v1.applyMatrix3(this),v1.toArray(array,j);return array}}(),applyToBuffer:function(){var v1;return function(buffer,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=buffer.length/buffer.itemSize);for(var i=0,j=offset;length>i;i++,j++)v1.x=buffer.getX(j),v1.y=buffer.getY(j),v1.z=buffer.getZ(j),v1.applyMatrix3(this),buffer.setXYZ(v1.x,v1.y,v1.z);return buffer}}(),multiplyScalar:function(s){var te=this.elements;return te[0]*=s,te[3]*=s,te[6]*=s,te[1]*=s,te[4]*=s,te[7]*=s,te[2]*=s,te[5]*=s,te[8]*=s,this},determinant:function(){var te=this.elements,a=te[0],b=te[1],c=te[2],d=te[3],e=te[4],f=te[5],g=te[6],h=te[7],i=te[8];return a*e*i-a*f*h-b*d*i+b*f*g+c*d*h-c*e*g},getInverse:function(matrix,throwOnInvertible){var me=matrix.elements,te=this.elements;te[0]=me[10]*me[5]-me[6]*me[9],te[1]=-me[10]*me[1]+me[2]*me[9],te[2]=me[6]*me[1]-me[2]*me[5],te[3]=-me[10]*me[4]+me[6]*me[8],te[4]=me[10]*me[0]-me[2]*me[8],te[5]=-me[6]*me[0]+me[2]*me[4],te[6]=me[9]*me[4]-me[5]*me[8],te[7]=-me[9]*me[0]+me[1]*me[8],te[8]=me[5]*me[0]-me[1]*me[4];var det=me[0]*te[0]+me[1]*te[3]+me[2]*te[6];if(0===det){var msg="Matrix3.getInverse(): can't invert matrix, determinant is 0";if(throwOnInvertible)throw new Error(msg);return console.warn(msg),this.identity(),this}return this.multiplyScalar(1/det),this},transpose:function(){var tmp,m=this.elements;return tmp=m[1],m[1]=m[3],m[3]=tmp,tmp=m[2],m[2]=m[6],m[6]=tmp,tmp=m[5],m[5]=m[7],m[7]=tmp,this},flattenToArrayOffset:function(array,offset){var te=this.elements;return array[offset]=te[0],array[offset+1]=te[1],array[offset+2]=te[2],array[offset+3]=te[3],array[offset+4]=te[4],array[offset+5]=te[5],array[offset+6]=te[6],array[offset+7]=te[7],array[offset+8]=te[8],array},getNormalMatrix:function(m){return this.getInverse(m).transpose(),this},transposeIntoArray:function(r){var m=this.elements;return r[0]=m[0],r[1]=m[3],r[2]=m[6],r[3]=m[1],r[4]=m[4],r[5]=m[7],r[6]=m[2],r[7]=m[5],r[8]=m[8],this},fromArray:function(array){return this.elements.set(array),this},toArray:function(){var te=this.elements;return[te[0],te[1],te[2],te[3],te[4],te[5],te[6],te[7],te[8]]}},THREE.Matrix4=function(){this.elements=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),arguments.length>0&&console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.")},THREE.Matrix4.prototype={constructor:THREE.Matrix4,set:function(n11,n12,n13,n14,n21,n22,n23,n24,n31,n32,n33,n34,n41,n42,n43,n44){var te=this.elements;return te[0]=n11,te[4]=n12,te[8]=n13,te[12]=n14,te[1]=n21,te[5]=n22,te[9]=n23,te[13]=n24,te[2]=n31,te[6]=n32,te[10]=n33,te[14]=n34,te[3]=n41,te[7]=n42,te[11]=n43,te[15]=n44,this},identity:function(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this},clone:function(){return(new THREE.Matrix4).fromArray(this.elements)},copy:function(m){return this.elements.set(m.elements),this},extractPosition:function(m){return console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()."),this.copyPosition(m)},copyPosition:function(m){var te=this.elements,me=m.elements;return te[12]=me[12],te[13]=me[13],te[14]=me[14],this},extractBasis:function(xAxis,yAxis,zAxis){var te=this.elements;return xAxis.set(te[0],te[1],te[2]),yAxis.set(te[4],te[5],te[6]),zAxis.set(te[8],te[9],te[10]),this},makeBasis:function(xAxis,yAxis,zAxis){return this.set(xAxis.x,yAxis.x,zAxis.x,0,xAxis.y,yAxis.y,zAxis.y,0,xAxis.z,yAxis.z,zAxis.z,0,0,0,0,1),this},extractRotation:function(){var v1;return function(m){void 0===v1&&(v1=new THREE.Vector3);var te=this.elements,me=m.elements,scaleX=1/v1.set(me[0],me[1],me[2]).length(),scaleY=1/v1.set(me[4],me[5],me[6]).length(),scaleZ=1/v1.set(me[8],me[9],me[10]).length();return te[0]=me[0]*scaleX,te[1]=me[1]*scaleX,te[2]=me[2]*scaleX,te[4]=me[4]*scaleY,te[5]=me[5]*scaleY,te[6]=me[6]*scaleY,te[8]=me[8]*scaleZ,te[9]=me[9]*scaleZ,te[10]=me[10]*scaleZ,this}}(),makeRotationFromEuler:function(euler){euler instanceof THREE.Euler==!1&&console.error("THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var te=this.elements,x=euler.x,y=euler.y,z=euler.z,a=Math.cos(x),b=Math.sin(x),c=Math.cos(y),d=Math.sin(y),e=Math.cos(z),f=Math.sin(z);if("XYZ"===euler.order){var ae=a*e,af=a*f,be=b*e,bf=b*f;te[0]=c*e,te[4]=-c*f,te[8]=d,te[1]=af+be*d,te[5]=ae-bf*d,te[9]=-b*c,te[2]=bf-ae*d,te[6]=be+af*d,te[10]=a*c}else if("YXZ"===euler.order){var ce=c*e,cf=c*f,de=d*e,df=d*f;te[0]=ce+df*b,te[4]=de*b-cf,te[8]=a*d,te[1]=a*f,te[5]=a*e,te[9]=-b,te[2]=cf*b-de,te[6]=df+ce*b,te[10]=a*c}else if("ZXY"===euler.order){var ce=c*e,cf=c*f,de=d*e,df=d*f;te[0]=ce-df*b,te[4]=-a*f,te[8]=de+cf*b,te[1]=cf+de*b,te[5]=a*e,te[9]=df-ce*b,te[2]=-a*d,te[6]=b,te[10]=a*c}else if("ZYX"===euler.order){var ae=a*e,af=a*f,be=b*e,bf=b*f;te[0]=c*e,te[4]=be*d-af,te[8]=ae*d+bf,te[1]=c*f,te[5]=bf*d+ae,te[9]=af*d-be,te[2]=-d,te[6]=b*c,te[10]=a*c}else if("YZX"===euler.order){var ac=a*c,ad=a*d,bc=b*c,bd=b*d;te[0]=c*e,te[4]=bd-ac*f,te[8]=bc*f+ad,te[1]=f,te[5]=a*e,te[9]=-b*e,te[2]=-d*e,te[6]=ad*f+bc,te[10]=ac-bd*f}else if("XZY"===euler.order){var ac=a*c,ad=a*d,bc=b*c,bd=b*d;te[0]=c*e,te[4]=-f,te[8]=d*e,te[1]=ac*f+bd,te[5]=a*e,te[9]=ad*f-bc,te[2]=bc*f-ad,te[6]=b*e,te[10]=bd*f+ac}return te[3]=0,te[7]=0,te[11]=0,te[12]=0,te[13]=0,te[14]=0,te[15]=1,this},setRotationFromQuaternion:function(q){return console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()."),this.makeRotationFromQuaternion(q)},makeRotationFromQuaternion:function(q){var te=this.elements,x=q.x,y=q.y,z=q.z,w=q.w,x2=x+x,y2=y+y,z2=z+z,xx=x*x2,xy=x*y2,xz=x*z2,yy=y*y2,yz=y*z2,zz=z*z2,wx=w*x2,wy=w*y2,wz=w*z2;return te[0]=1-(yy+zz),te[4]=xy-wz,te[8]=xz+wy,te[1]=xy+wz,te[5]=1-(xx+zz),te[9]=yz-wx,te[2]=xz-wy,te[6]=yz+wx,te[10]=1-(xx+yy),te[3]=0,te[7]=0,te[11]=0,te[12]=0,te[13]=0,te[14]=0,te[15]=1,this},lookAt:function(){var x,y,z;return function(eye,target,up){void 0===x&&(x=new THREE.Vector3),void 0===y&&(y=new THREE.Vector3),void 0===z&&(z=new THREE.Vector3);var te=this.elements;return z.subVectors(eye,target).normalize(),0===z.lengthSq()&&(z.z=1),x.crossVectors(up,z).normalize(),0===x.lengthSq()&&(z.x+=1e-4,x.crossVectors(up,z).normalize()),y.crossVectors(z,x),te[0]=x.x,te[4]=y.x,te[8]=z.x,te[1]=x.y,te[5]=y.y,te[9]=z.y,te[2]=x.z,te[6]=y.z,te[10]=z.z,this}}(),multiply:function(m,n){return void 0!==n?(console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(m,n)):this.multiplyMatrices(this,m)},multiplyMatrices:function(a,b){var ae=a.elements,be=b.elements,te=this.elements,a11=ae[0],a12=ae[4],a13=ae[8],a14=ae[12],a21=ae[1],a22=ae[5],a23=ae[9],a24=ae[13],a31=ae[2],a32=ae[6],a33=ae[10],a34=ae[14],a41=ae[3],a42=ae[7],a43=ae[11],a44=ae[15],b11=be[0],b12=be[4],b13=be[8],b14=be[12],b21=be[1],b22=be[5],b23=be[9],b24=be[13],b31=be[2],b32=be[6],b33=be[10],b34=be[14],b41=be[3],b42=be[7],b43=be[11],b44=be[15];return te[0]=a11*b11+a12*b21+a13*b31+a14*b41,te[4]=a11*b12+a12*b22+a13*b32+a14*b42,te[8]=a11*b13+a12*b23+a13*b33+a14*b43,te[12]=a11*b14+a12*b24+a13*b34+a14*b44,te[1]=a21*b11+a22*b21+a23*b31+a24*b41,te[5]=a21*b12+a22*b22+a23*b32+a24*b42,te[9]=a21*b13+a22*b23+a23*b33+a24*b43,te[13]=a21*b14+a22*b24+a23*b34+a24*b44,te[2]=a31*b11+a32*b21+a33*b31+a34*b41,te[6]=a31*b12+a32*b22+a33*b32+a34*b42,te[10]=a31*b13+a32*b23+a33*b33+a34*b43,te[14]=a31*b14+a32*b24+a33*b34+a34*b44,te[3]=a41*b11+a42*b21+a43*b31+a44*b41,te[7]=a41*b12+a42*b22+a43*b32+a44*b42,te[11]=a41*b13+a42*b23+a43*b33+a44*b43,te[15]=a41*b14+a42*b24+a43*b34+a44*b44,this},multiplyToArray:function(a,b,r){var te=this.elements;return this.multiplyMatrices(a,b),r[0]=te[0],r[1]=te[1],r[2]=te[2],r[3]=te[3],r[4]=te[4],r[5]=te[5],r[6]=te[6],r[7]=te[7],r[8]=te[8],r[9]=te[9],r[10]=te[10],r[11]=te[11],r[12]=te[12],r[13]=te[13],r[14]=te[14],r[15]=te[15],this},multiplyScalar:function(s){var te=this.elements;return te[0]*=s,te[4]*=s,te[8]*=s,te[12]*=s,te[1]*=s,te[5]*=s,te[9]*=s,te[13]*=s,te[2]*=s,te[6]*=s,te[10]*=s,te[14]*=s,te[3]*=s,te[7]*=s,te[11]*=s,te[15]*=s,this},multiplyVector3:function(vector){return console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead."),vector.applyProjection(this)},multiplyVector4:function(vector){return console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead."),vector.applyMatrix4(this)},multiplyVector3Array:function(a){return console.warn("THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead."),this.applyToVector3Array(a)},applyToVector3Array:function(){var v1;return function(array,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=array.length);for(var i=0,j=offset;length>i;i+=3,j+=3)v1.fromArray(array,j),v1.applyMatrix4(this),v1.toArray(array,j);return array}}(),applyToBuffer:function(){var v1;return function(buffer,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=buffer.length/buffer.itemSize);for(var i=0,j=offset;length>i;i++,j++)v1.x=buffer.getX(j),v1.y=buffer.getY(j),v1.z=buffer.getZ(j),v1.applyMatrix4(this),buffer.setXYZ(v1.x,v1.y,v1.z);return buffer}}(),rotateAxis:function(v){console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead."),v.transformDirection(this)},crossVector:function(vector){return console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead."),vector.applyMatrix4(this)},determinant:function(){var te=this.elements,n11=te[0],n12=te[4],n13=te[8],n14=te[12],n21=te[1],n22=te[5],n23=te[9],n24=te[13],n31=te[2],n32=te[6],n33=te[10],n34=te[14],n41=te[3],n42=te[7],n43=te[11],n44=te[15];return n41*(+n14*n23*n32-n13*n24*n32-n14*n22*n33+n12*n24*n33+n13*n22*n34-n12*n23*n34)+n42*(+n11*n23*n34-n11*n24*n33+n14*n21*n33-n13*n21*n34+n13*n24*n31-n14*n23*n31)+n43*(+n11*n24*n32-n11*n22*n34-n14*n21*n32+n12*n21*n34+n14*n22*n31-n12*n24*n31)+n44*(-n13*n22*n31-n11*n23*n32+n11*n22*n33+n13*n21*n32-n12*n21*n33+n12*n23*n31)},transpose:function(){var tmp,te=this.elements;return tmp=te[1],te[1]=te[4],te[4]=tmp,tmp=te[2],te[2]=te[8],te[8]=tmp,tmp=te[6],te[6]=te[9],te[9]=tmp,tmp=te[3],te[3]=te[12],te[12]=tmp,tmp=te[7],te[7]=te[13],te[13]=tmp,tmp=te[11],te[11]=te[14],te[14]=tmp,this},flattenToArrayOffset:function(array,offset){var te=this.elements;return array[offset]=te[0],array[offset+1]=te[1],array[offset+2]=te[2],array[offset+3]=te[3],array[offset+4]=te[4],array[offset+5]=te[5],array[offset+6]=te[6],array[offset+7]=te[7],array[offset+8]=te[8],array[offset+9]=te[9],array[offset+10]=te[10],array[offset+11]=te[11],array[offset+12]=te[12],array[offset+13]=te[13],array[offset+14]=te[14],array[offset+15]=te[15],array},getPosition:function(){var v1;return function(){void 0===v1&&(v1=new THREE.Vector3),console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.");var te=this.elements;return v1.set(te[12],te[13],te[14])}}(),setPosition:function(v){var te=this.elements;return te[12]=v.x,te[13]=v.y,te[14]=v.z,this},getInverse:function(m,throwOnInvertible){var te=this.elements,me=m.elements,n11=me[0],n12=me[4],n13=me[8],n14=me[12],n21=me[1],n22=me[5],n23=me[9],n24=me[13],n31=me[2],n32=me[6],n33=me[10],n34=me[14],n41=me[3],n42=me[7],n43=me[11],n44=me[15];te[0]=n23*n34*n42-n24*n33*n42+n24*n32*n43-n22*n34*n43-n23*n32*n44+n22*n33*n44,te[4]=n14*n33*n42-n13*n34*n42-n14*n32*n43+n12*n34*n43+n13*n32*n44-n12*n33*n44,te[8]=n13*n24*n42-n14*n23*n42+n14*n22*n43-n12*n24*n43-n13*n22*n44+n12*n23*n44,te[12]=n14*n23*n32-n13*n24*n32-n14*n22*n33+n12*n24*n33+n13*n22*n34-n12*n23*n34,te[1]=n24*n33*n41-n23*n34*n41-n24*n31*n43+n21*n34*n43+n23*n31*n44-n21*n33*n44,te[5]=n13*n34*n41-n14*n33*n41+n14*n31*n43-n11*n34*n43-n13*n31*n44+n11*n33*n44,te[9]=n14*n23*n41-n13*n24*n41-n14*n21*n43+n11*n24*n43+n13*n21*n44-n11*n23*n44,te[13]=n13*n24*n31-n14*n23*n31+n14*n21*n33-n11*n24*n33-n13*n21*n34+n11*n23*n34,te[2]=n22*n34*n41-n24*n32*n41+n24*n31*n42-n21*n34*n42-n22*n31*n44+n21*n32*n44,te[6]=n14*n32*n41-n12*n34*n41-n14*n31*n42+n11*n34*n42+n12*n31*n44-n11*n32*n44,te[10]=n12*n24*n41-n14*n22*n41+n14*n21*n42-n11*n24*n42-n12*n21*n44+n11*n22*n44,te[14]=n14*n22*n31-n12*n24*n31-n14*n21*n32+n11*n24*n32+n12*n21*n34-n11*n22*n34,te[3]=n23*n32*n41-n22*n33*n41-n23*n31*n42+n21*n33*n42+n22*n31*n43-n21*n32*n43,te[7]=n12*n33*n41-n13*n32*n41+n13*n31*n42-n11*n33*n42-n12*n31*n43+n11*n32*n43,te[11]=n13*n22*n41-n12*n23*n41-n13*n21*n42+n11*n23*n42+n12*n21*n43-n11*n22*n43,te[15]=n12*n23*n31-n13*n22*n31+n13*n21*n32-n11*n23*n32-n12*n21*n33+n11*n22*n33;var det=n11*te[0]+n21*te[4]+n31*te[8]+n41*te[12];if(0===det){var msg="THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0";if(throwOnInvertible)throw new Error(msg);return console.warn(msg),this.identity(),this}return this.multiplyScalar(1/det),this},translate:function(v){console.error("THREE.Matrix4: .translate() has been removed.")},rotateX:function(angle){console.error("THREE.Matrix4: .rotateX() has been removed.")},rotateY:function(angle){console.error("THREE.Matrix4: .rotateY() has been removed.")},rotateZ:function(angle){console.error("THREE.Matrix4: .rotateZ() has been removed.")},rotateByAxis:function(axis,angle){console.error("THREE.Matrix4: .rotateByAxis() has been removed.")},scale:function(v){var te=this.elements,x=v.x,y=v.y,z=v.z;return te[0]*=x,te[4]*=y,te[8]*=z,te[1]*=x,te[5]*=y,te[9]*=z,te[2]*=x,te[6]*=y,te[10]*=z,te[3]*=x,te[7]*=y,te[11]*=z,this},getMaxScaleOnAxis:function(){var te=this.elements,scaleXSq=te[0]*te[0]+te[1]*te[1]+te[2]*te[2],scaleYSq=te[4]*te[4]+te[5]*te[5]+te[6]*te[6],scaleZSq=te[8]*te[8]+te[9]*te[9]+te[10]*te[10];return Math.sqrt(Math.max(scaleXSq,scaleYSq,scaleZSq))},makeTranslation:function(x,y,z){return this.set(1,0,0,x,0,1,0,y,0,0,1,z,0,0,0,1),this},makeRotationX:function(theta){var c=Math.cos(theta),s=Math.sin(theta);return this.set(1,0,0,0,0,c,-s,0,0,s,c,0,0,0,0,1),this},makeRotationY:function(theta){var c=Math.cos(theta),s=Math.sin(theta);return this.set(c,0,s,0,0,1,0,0,-s,0,c,0,0,0,0,1),this},makeRotationZ:function(theta){var c=Math.cos(theta),s=Math.sin(theta);return this.set(c,-s,0,0,s,c,0,0,0,0,1,0,0,0,0,1),this},makeRotationAxis:function(axis,angle){var c=Math.cos(angle),s=Math.sin(angle),t=1-c,x=axis.x,y=axis.y,z=axis.z,tx=t*x,ty=t*y;return this.set(tx*x+c,tx*y-s*z,tx*z+s*y,0,tx*y+s*z,ty*y+c,ty*z-s*x,0,tx*z-s*y,ty*z+s*x,t*z*z+c,0,0,0,0,1),this},makeScale:function(x,y,z){return this.set(x,0,0,0,0,y,0,0,0,0,z,0,0,0,0,1),this},compose:function(position,quaternion,scale){return this.makeRotationFromQuaternion(quaternion),this.scale(scale),this.setPosition(position),this},decompose:function(){var vector,matrix;return function(position,quaternion,scale){void 0===vector&&(vector=new THREE.Vector3),void 0===matrix&&(matrix=new THREE.Matrix4);var te=this.elements,sx=vector.set(te[0],te[1],te[2]).length(),sy=vector.set(te[4],te[5],te[6]).length(),sz=vector.set(te[8],te[9],te[10]).length(),det=this.determinant();0>det&&(sx=-sx),position.x=te[12],position.y=te[13],position.z=te[14],matrix.elements.set(this.elements);var invSX=1/sx,invSY=1/sy,invSZ=1/sz;return matrix.elements[0]*=invSX,matrix.elements[1]*=invSX,matrix.elements[2]*=invSX,matrix.elements[4]*=invSY,matrix.elements[5]*=invSY,matrix.elements[6]*=invSY,matrix.elements[8]*=invSZ,matrix.elements[9]*=invSZ,matrix.elements[10]*=invSZ,quaternion.setFromRotationMatrix(matrix),scale.x=sx,scale.y=sy,scale.z=sz,this}}(),makeFrustum:function(left,right,bottom,top,near,far){var te=this.elements,x=2*near/(right-left),y=2*near/(top-bottom),a=(right+left)/(right-left),b=(top+bottom)/(top-bottom),c=-(far+near)/(far-near),d=-2*far*near/(far-near);return te[0]=x,te[4]=0,te[8]=a,te[12]=0,te[1]=0,te[5]=y,te[9]=b,te[13]=0,te[2]=0,te[6]=0,te[10]=c,te[14]=d,te[3]=0,te[7]=0,te[11]=-1,te[15]=0,this},makePerspective:function(fov,aspect,near,far){var ymax=near*Math.tan(THREE.Math.degToRad(.5*fov)),ymin=-ymax,xmin=ymin*aspect,xmax=ymax*aspect;return this.makeFrustum(xmin,xmax,ymin,ymax,near,far)},makeOrthographic:function(left,right,top,bottom,near,far){var te=this.elements,w=right-left,h=top-bottom,p=far-near,x=(right+left)/w,y=(top+bottom)/h,z=(far+near)/p;return te[0]=2/w,te[4]=0,te[8]=0,te[12]=-x,te[1]=0,te[5]=2/h,te[9]=0,te[13]=-y,te[2]=0,te[6]=0,te[10]=-2/p,te[14]=-z,te[3]=0,te[7]=0,te[11]=0,te[15]=1,this},equals:function(matrix){for(var te=this.elements,me=matrix.elements,i=0;16>i;i++)if(te[i]!==me[i])return!1;return!0},fromArray:function(array){return this.elements.set(array),this},toArray:function(){var te=this.elements;return[te[0],te[1],te[2],te[3],te[4],te[5],te[6],te[7],te[8],te[9],te[10],te[11],te[12],te[13],te[14],te[15]]}},THREE.Ray=function(origin,direction){this.origin=void 0!==origin?origin:new THREE.Vector3,this.direction=void 0!==direction?direction:new THREE.Vector3},THREE.Ray.prototype={constructor:THREE.Ray,set:function(origin,direction){return this.origin.copy(origin),this.direction.copy(direction),this},clone:function(){return(new this.constructor).copy(this)},copy:function(ray){return this.origin.copy(ray.origin),this.direction.copy(ray.direction),this},at:function(t,optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.copy(this.direction).multiplyScalar(t).add(this.origin)},recast:function(){var v1=new THREE.Vector3;return function(t){return this.origin.copy(this.at(t,v1)),this}}(),closestPointToPoint:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector3;result.subVectors(point,this.origin);var directionDistance=result.dot(this.direction);return 0>directionDistance?result.copy(this.origin):result.copy(this.direction).multiplyScalar(directionDistance).add(this.origin)},distanceToPoint:function(point){return Math.sqrt(this.distanceSqToPoint(point))},distanceSqToPoint:function(){var v1=new THREE.Vector3;return function(point){var directionDistance=v1.subVectors(point,this.origin).dot(this.direction);return 0>directionDistance?this.origin.distanceToSquared(point):(v1.copy(this.direction).multiplyScalar(directionDistance).add(this.origin),v1.distanceToSquared(point))}}(),distanceSqToSegment:function(){var segCenter=new THREE.Vector3,segDir=new THREE.Vector3,diff=new THREE.Vector3;return function(v0,v1,optionalPointOnRay,optionalPointOnSegment){segCenter.copy(v0).add(v1).multiplyScalar(.5),segDir.copy(v1).sub(v0).normalize(),diff.copy(this.origin).sub(segCenter);var s0,s1,sqrDist,extDet,segExtent=.5*v0.distanceTo(v1),a01=-this.direction.dot(segDir),b0=diff.dot(this.direction),b1=-diff.dot(segDir),c=diff.lengthSq(),det=Math.abs(1-a01*a01);if(det>0)if(s0=a01*b1-b0,s1=a01*b0-b1,extDet=segExtent*det,s0>=0)if(s1>=-extDet)if(extDet>=s1){var invDet=1/det;s0*=invDet,s1*=invDet,sqrDist=s0*(s0+a01*s1+2*b0)+s1*(a01*s0+s1+2*b1)+c}else s1=segExtent,s0=Math.max(0,-(a01*s1+b0)),sqrDist=-s0*s0+s1*(s1+2*b1)+c;else s1=-segExtent,s0=Math.max(0,-(a01*s1+b0)),sqrDist=-s0*s0+s1*(s1+2*b1)+c;else-extDet>=s1?(s0=Math.max(0,-(-a01*segExtent+b0)),s1=s0>0?-segExtent:Math.min(Math.max(-segExtent,-b1),segExtent),sqrDist=-s0*s0+s1*(s1+2*b1)+c):extDet>=s1?(s0=0,s1=Math.min(Math.max(-segExtent,-b1),segExtent),sqrDist=s1*(s1+2*b1)+c):(s0=Math.max(0,-(a01*segExtent+b0)),s1=s0>0?segExtent:Math.min(Math.max(-segExtent,-b1),segExtent),sqrDist=-s0*s0+s1*(s1+2*b1)+c);else s1=a01>0?-segExtent:segExtent,s0=Math.max(0,-(a01*s1+b0)),sqrDist=-s0*s0+s1*(s1+2*b1)+c;return optionalPointOnRay&&optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin),optionalPointOnSegment&&optionalPointOnSegment.copy(segDir).multiplyScalar(s1).add(segCenter),sqrDist}}(),isIntersectionSphere:function(sphere){return this.distanceToPoint(sphere.center)<=sphere.radius},intersectSphere:function(){var v1=new THREE.Vector3;return function(sphere,optionalTarget){v1.subVectors(sphere.center,this.origin);var tca=v1.dot(this.direction),d2=v1.dot(v1)-tca*tca,radius2=sphere.radius*sphere.radius;if(d2>radius2)return null;var thc=Math.sqrt(radius2-d2),t0=tca-thc,t1=tca+thc;return 0>t0&&0>t1?null:0>t0?this.at(t1,optionalTarget):this.at(t0,optionalTarget)}}(),isIntersectionPlane:function(plane){var distToPoint=plane.distanceToPoint(this.origin);if(0===distToPoint)return!0;var denominator=plane.normal.dot(this.direction);return 0>denominator*distToPoint},distanceToPlane:function(plane){var denominator=plane.normal.dot(this.direction);if(0===denominator)return 0===plane.distanceToPoint(this.origin)?0:null;var t=-(this.origin.dot(plane.normal)+plane.constant)/denominator;return t>=0?t:null},intersectPlane:function(plane,optionalTarget){var t=this.distanceToPlane(plane);return null===t?null:this.at(t,optionalTarget)},isIntersectionBox:function(){var v=new THREE.Vector3;return function(box){return null!==this.intersectBox(box,v)}}(),intersectBox:function(box,optionalTarget){var tmin,tmax,tymin,tymax,tzmin,tzmax,invdirx=1/this.direction.x,invdiry=1/this.direction.y,invdirz=1/this.direction.z,origin=this.origin;return invdirx>=0?(tmin=(box.min.x-origin.x)*invdirx,tmax=(box.max.x-origin.x)*invdirx):(tmin=(box.max.x-origin.x)*invdirx,tmax=(box.min.x-origin.x)*invdirx),invdiry>=0?(tymin=(box.min.y-origin.y)*invdiry,tymax=(box.max.y-origin.y)*invdiry):(tymin=(box.max.y-origin.y)*invdiry,tymax=(box.min.y-origin.y)*invdiry),tmin>tymax||tymin>tmax?null:((tymin>tmin||tmin!==tmin)&&(tmin=tymin),(tmax>tymax||tmax!==tmax)&&(tmax=tymax),invdirz>=0?(tzmin=(box.min.z-origin.z)*invdirz,tzmax=(box.max.z-origin.z)*invdirz):(tzmin=(box.max.z-origin.z)*invdirz,tzmax=(box.min.z-origin.z)*invdirz),tmin>tzmax||tzmin>tmax?null:((tzmin>tmin||tmin!==tmin)&&(tmin=tzmin),(tmax>tzmax||tmax!==tmax)&&(tmax=tzmax),0>tmax?null:this.at(tmin>=0?tmin:tmax,optionalTarget)))},intersectTriangle:function(){var diff=new THREE.Vector3,edge1=new THREE.Vector3,edge2=new THREE.Vector3,normal=new THREE.Vector3;return function(a,b,c,backfaceCulling,optionalTarget){edge1.subVectors(b,a),edge2.subVectors(c,a),normal.crossVectors(edge1,edge2);var sign,DdN=this.direction.dot(normal);if(DdN>0){if(backfaceCulling)return null;sign=1}else{if(!(0>DdN))return null;sign=-1,DdN=-DdN}diff.subVectors(this.origin,a);var DdQxE2=sign*this.direction.dot(edge2.crossVectors(diff,edge2));if(0>DdQxE2)return null;var DdE1xQ=sign*this.direction.dot(edge1.cross(diff));if(0>DdE1xQ)return null;if(DdQxE2+DdE1xQ>DdN)return null;var QdN=-sign*diff.dot(normal);return 0>QdN?null:this.at(QdN/DdN,optionalTarget)}}(),applyMatrix4:function(matrix4){return this.direction.add(this.origin).applyMatrix4(matrix4),this.origin.applyMatrix4(matrix4),this.direction.sub(this.origin),this.direction.normalize(),this},equals:function(ray){return ray.origin.equals(this.origin)&&ray.direction.equals(this.direction)}},THREE.Sphere=function(center,radius){this.center=void 0!==center?center:new THREE.Vector3,this.radius=void 0!==radius?radius:0},THREE.Sphere.prototype={constructor:THREE.Sphere,set:function(center,radius){return this.center.copy(center),this.radius=radius,this},setFromPoints:function(){var box=new THREE.Box3;return function(points,optionalCenter){var center=this.center;void 0!==optionalCenter?center.copy(optionalCenter):box.setFromPoints(points).center(center);for(var maxRadiusSq=0,i=0,il=points.length;il>i;i++)maxRadiusSq=Math.max(maxRadiusSq,center.distanceToSquared(points[i]));return this.radius=Math.sqrt(maxRadiusSq),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(sphere){return this.center.copy(sphere.center),this.radius=sphere.radius,this},empty:function(){return this.radius<=0},containsPoint:function(point){return point.distanceToSquared(this.center)<=this.radius*this.radius},distanceToPoint:function(point){return point.distanceTo(this.center)-this.radius},intersectsSphere:function(sphere){var radiusSum=this.radius+sphere.radius;return sphere.center.distanceToSquared(this.center)<=radiusSum*radiusSum},clampPoint:function(point,optionalTarget){var deltaLengthSq=this.center.distanceToSquared(point),result=optionalTarget||new THREE.Vector3;return result.copy(point),deltaLengthSq>this.radius*this.radius&&(result.sub(this.center).normalize(),result.multiplyScalar(this.radius).add(this.center)),result},getBoundingBox:function(optionalTarget){var box=optionalTarget||new THREE.Box3;return box.set(this.center,this.center),box.expandByScalar(this.radius),box},applyMatrix4:function(matrix){return this.center.applyMatrix4(matrix),this.radius=this.radius*matrix.getMaxScaleOnAxis(),this},translate:function(offset){return this.center.add(offset),this},equals:function(sphere){return sphere.center.equals(this.center)&&sphere.radius===this.radius}},THREE.Frustum=function(p0,p1,p2,p3,p4,p5){this.planes=[void 0!==p0?p0:new THREE.Plane,void 0!==p1?p1:new THREE.Plane,void 0!==p2?p2:new THREE.Plane,void 0!==p3?p3:new THREE.Plane,void 0!==p4?p4:new THREE.Plane,void 0!==p5?p5:new THREE.Plane]},THREE.Frustum.prototype={constructor:THREE.Frustum,set:function(p0,p1,p2,p3,p4,p5){var planes=this.planes;return planes[0].copy(p0),planes[1].copy(p1),planes[2].copy(p2),planes[3].copy(p3),planes[4].copy(p4),planes[5].copy(p5),this},clone:function(){return(new this.constructor).copy(this)},copy:function(frustum){for(var planes=this.planes,i=0;6>i;i++)planes[i].copy(frustum.planes[i]);return this},setFromMatrix:function(m){var planes=this.planes,me=m.elements,me0=me[0],me1=me[1],me2=me[2],me3=me[3],me4=me[4],me5=me[5],me6=me[6],me7=me[7],me8=me[8],me9=me[9],me10=me[10],me11=me[11],me12=me[12],me13=me[13],me14=me[14],me15=me[15];return planes[0].setComponents(me3-me0,me7-me4,me11-me8,me15-me12).normalize(),planes[1].setComponents(me3+me0,me7+me4,me11+me8,me15+me12).normalize(),planes[2].setComponents(me3+me1,me7+me5,me11+me9,me15+me13).normalize(),planes[3].setComponents(me3-me1,me7-me5,me11-me9,me15-me13).normalize(),planes[4].setComponents(me3-me2,me7-me6,me11-me10,me15-me14).normalize(),planes[5].setComponents(me3+me2,me7+me6,me11+me10,me15+me14).normalize(),this},intersectsObject:function(){var sphere=new THREE.Sphere;return function(object){var geometry=object.geometry;return null===geometry.boundingSphere&&geometry.computeBoundingSphere(),sphere.copy(geometry.boundingSphere),sphere.applyMatrix4(object.matrixWorld),this.intersectsSphere(sphere)}}(),intersectsSphere:function(sphere){for(var planes=this.planes,center=sphere.center,negRadius=-sphere.radius,i=0;6>i;i++){var distance=planes[i].distanceToPoint(center);if(negRadius>distance)return!1}return!0},intersectsBox:function(){var p1=new THREE.Vector3,p2=new THREE.Vector3;return function(box){for(var planes=this.planes,i=0;6>i;i++){var plane=planes[i];p1.x=plane.normal.x>0?box.min.x:box.max.x,p2.x=plane.normal.x>0?box.max.x:box.min.x,p1.y=plane.normal.y>0?box.min.y:box.max.y,p2.y=plane.normal.y>0?box.max.y:box.min.y,p1.z=plane.normal.z>0?box.min.z:box.max.z,p2.z=plane.normal.z>0?box.max.z:box.min.z;var d1=plane.distanceToPoint(p1),d2=plane.distanceToPoint(p2);if(0>d1&&0>d2)return!1}return!0}}(),containsPoint:function(point){for(var planes=this.planes,i=0;6>i;i++)if(planes[i].distanceToPoint(point)<0)return!1;return!0}},THREE.Plane=function(normal,constant){this.normal=void 0!==normal?normal:new THREE.Vector3(1,0,0),this.constant=void 0!==constant?constant:0},THREE.Plane.prototype={constructor:THREE.Plane,set:function(normal,constant){return this.normal.copy(normal),this.constant=constant,this},setComponents:function(x,y,z,w){return this.normal.set(x,y,z),this.constant=w,this},setFromNormalAndCoplanarPoint:function(normal,point){return this.normal.copy(normal),this.constant=-point.dot(this.normal),this},setFromCoplanarPoints:function(){var v1=new THREE.Vector3,v2=new THREE.Vector3;return function(a,b,c){var normal=v1.subVectors(c,b).cross(v2.subVectors(a,b)).normalize();return this.setFromNormalAndCoplanarPoint(normal,a),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(plane){return this.normal.copy(plane.normal),this.constant=plane.constant,this},normalize:function(){var inverseNormalLength=1/this.normal.length();return this.normal.multiplyScalar(inverseNormalLength),this.constant*=inverseNormalLength,this},negate:function(){return this.constant*=-1,this.normal.negate(),this},distanceToPoint:function(point){return this.normal.dot(point)+this.constant},distanceToSphere:function(sphere){return this.distanceToPoint(sphere.center)-sphere.radius},projectPoint:function(point,optionalTarget){return this.orthoPoint(point,optionalTarget).sub(point).negate()},orthoPoint:function(point,optionalTarget){var perpendicularMagnitude=this.distanceToPoint(point),result=optionalTarget||new THREE.Vector3;return result.copy(this.normal).multiplyScalar(perpendicularMagnitude)},isIntersectionLine:function(line){var startSign=this.distanceToPoint(line.start),endSign=this.distanceToPoint(line.end);return 0>startSign&&endSign>0||0>endSign&&startSign>0},intersectLine:function(){var v1=new THREE.Vector3;return function(line,optionalTarget){var result=optionalTarget||new THREE.Vector3,direction=line.delta(v1),denominator=this.normal.dot(direction);if(0!==denominator){var t=-(line.start.dot(this.normal)+this.constant)/denominator;if(!(0>t||t>1))return result.copy(direction).multiplyScalar(t).add(line.start);
+}else if(0===this.distanceToPoint(line.start))return result.copy(line.start)}}(),coplanarPoint:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.copy(this.normal).multiplyScalar(-this.constant)},applyMatrix4:function(){var v1=new THREE.Vector3,v2=new THREE.Vector3,m1=new THREE.Matrix3;return function(matrix,optionalNormalMatrix){var normalMatrix=optionalNormalMatrix||m1.getNormalMatrix(matrix),newNormal=v1.copy(this.normal).applyMatrix3(normalMatrix),newCoplanarPoint=this.coplanarPoint(v2);return newCoplanarPoint.applyMatrix4(matrix),this.setFromNormalAndCoplanarPoint(newNormal,newCoplanarPoint),this}}(),translate:function(offset){return this.constant=this.constant-offset.dot(this.normal),this},equals:function(plane){return plane.normal.equals(this.normal)&&plane.constant===this.constant}},THREE.Math={generateUUID:function(){var r,chars="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),uuid=new Array(36),rnd=0;return function(){for(var i=0;36>i;i++)8===i||13===i||18===i||23===i?uuid[i]="-":14===i?uuid[i]="4":(2>=rnd&&(rnd=33554432+16777216*Math.random()|0),r=15&rnd,rnd>>=4,uuid[i]=chars[19===i?3&r|8:r]);return uuid.join("")}}(),clamp:function(value,min,max){return Math.max(min,Math.min(max,value))},euclideanModulo:function(n,m){return(n%m+m)%m},mapLinear:function(x,a1,a2,b1,b2){return b1+(x-a1)*(b2-b1)/(a2-a1)},smoothstep:function(x,min,max){return min>=x?0:x>=max?1:(x=(x-min)/(max-min),x*x*(3-2*x))},smootherstep:function(x,min,max){return min>=x?0:x>=max?1:(x=(x-min)/(max-min),x*x*x*(x*(6*x-15)+10))},random16:function(){return(65280*Math.random()+255*Math.random())/65535},randInt:function(low,high){return low+Math.floor(Math.random()*(high-low+1))},randFloat:function(low,high){return low+Math.random()*(high-low)},randFloatSpread:function(range){return range*(.5-Math.random())},degToRad:function(){var degreeToRadiansFactor=Math.PI/180;return function(degrees){return degrees*degreeToRadiansFactor}}(),radToDeg:function(){var radianToDegreesFactor=180/Math.PI;return function(radians){return radians*radianToDegreesFactor}}(),isPowerOfTwo:function(value){return 0===(value&value-1)&&0!==value},nearestPowerOfTwo:function(value){return Math.pow(2,Math.round(Math.log(value)/Math.LN2))},nextPowerOfTwo:function(value){return value--,value|=value>>1,value|=value>>2,value|=value>>4,value|=value>>8,value|=value>>16,value++,value}},THREE.Spline=function(points){function interpolate(p0,p1,p2,p3,t,t2,t3){var v0=.5*(p2-p0),v1=.5*(p3-p1);return(2*(p1-p2)+v0+v1)*t3+(-3*(p1-p2)-2*v0-v1)*t2+v0*t+p1}this.points=points;var point,intPoint,weight,w2,w3,pa,pb,pc,pd,c=[],v3={x:0,y:0,z:0};this.initFromArray=function(a){this.points=[];for(var i=0;ithis.points.length-2?this.points.length-1:intPoint+1,c[3]=intPoint>this.points.length-3?this.points.length-1:intPoint+2,pa=this.points[c[0]],pb=this.points[c[1]],pc=this.points[c[2]],pd=this.points[c[3]],w2=weight*weight,w3=weight*w2,v3.x=interpolate(pa.x,pb.x,pc.x,pd.x,weight,w2,w3),v3.y=interpolate(pa.y,pb.y,pc.y,pd.y,weight,w2,w3),v3.z=interpolate(pa.z,pb.z,pc.z,pd.z,weight,w2,w3),v3},this.getControlPointsArray=function(){var i,p,l=this.points.length,coords=[];for(i=0;l>i;i++)p=this.points[i],coords[i]=[p.x,p.y,p.z];return coords},this.getLength=function(nSubDivisions){var i,index,nSamples,position,point=0,intPoint=0,oldIntPoint=0,oldPosition=new THREE.Vector3,tmpVec=new THREE.Vector3,chunkLengths=[],totalLength=0;for(chunkLengths[0]=0,nSubDivisions||(nSubDivisions=100),nSamples=this.points.length*nSubDivisions,oldPosition.copy(this.points[0]),i=1;nSamples>i;i++)index=i/nSamples,position=this.getPoint(index),tmpVec.copy(position),totalLength+=tmpVec.distanceTo(oldPosition),oldPosition.copy(position),point=(this.points.length-1)*index,intPoint=Math.floor(point),intPoint!==oldIntPoint&&(chunkLengths[intPoint]=totalLength,oldIntPoint=intPoint);return chunkLengths[chunkLengths.length]=totalLength,{chunks:chunkLengths,total:totalLength}},this.reparametrizeByArcLength=function(samplingCoef){var i,j,index,indexCurrent,indexNext,realDistance,sampling,position,newpoints=[],tmpVec=new THREE.Vector3,sl=this.getLength();for(newpoints.push(tmpVec.copy(this.points[0]).clone()),i=1;ij;j++)index=indexCurrent+j*(1/sampling)*(indexNext-indexCurrent),position=this.getPoint(index),newpoints.push(tmpVec.copy(position).clone());newpoints.push(tmpVec.copy(this.points[i]).clone())}this.points=newpoints}},THREE.Triangle=function(a,b,c){this.a=void 0!==a?a:new THREE.Vector3,this.b=void 0!==b?b:new THREE.Vector3,this.c=void 0!==c?c:new THREE.Vector3},THREE.Triangle.normal=function(){var v0=new THREE.Vector3;return function(a,b,c,optionalTarget){var result=optionalTarget||new THREE.Vector3;result.subVectors(c,b),v0.subVectors(a,b),result.cross(v0);var resultLengthSq=result.lengthSq();return resultLengthSq>0?result.multiplyScalar(1/Math.sqrt(resultLengthSq)):result.set(0,0,0)}}(),THREE.Triangle.barycoordFromPoint=function(){var v0=new THREE.Vector3,v1=new THREE.Vector3,v2=new THREE.Vector3;return function(point,a,b,c,optionalTarget){v0.subVectors(c,a),v1.subVectors(b,a),v2.subVectors(point,a);var dot00=v0.dot(v0),dot01=v0.dot(v1),dot02=v0.dot(v2),dot11=v1.dot(v1),dot12=v1.dot(v2),denom=dot00*dot11-dot01*dot01,result=optionalTarget||new THREE.Vector3;if(0===denom)return result.set(-2,-1,-1);var invDenom=1/denom,u=(dot11*dot02-dot01*dot12)*invDenom,v=(dot00*dot12-dot01*dot02)*invDenom;return result.set(1-u-v,v,u)}}(),THREE.Triangle.containsPoint=function(){var v1=new THREE.Vector3;return function(point,a,b,c){var result=THREE.Triangle.barycoordFromPoint(point,a,b,c,v1);return result.x>=0&&result.y>=0&&result.x+result.y<=1}}(),THREE.Triangle.prototype={constructor:THREE.Triangle,set:function(a,b,c){return this.a.copy(a),this.b.copy(b),this.c.copy(c),this},setFromPointsAndIndices:function(points,i0,i1,i2){return this.a.copy(points[i0]),this.b.copy(points[i1]),this.c.copy(points[i2]),this},clone:function(){return(new this.constructor).copy(this)},copy:function(triangle){return this.a.copy(triangle.a),this.b.copy(triangle.b),this.c.copy(triangle.c),this},area:function(){var v0=new THREE.Vector3,v1=new THREE.Vector3;return function(){return v0.subVectors(this.c,this.b),v1.subVectors(this.a,this.b),.5*v0.cross(v1).length()}}(),midpoint:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},normal:function(optionalTarget){return THREE.Triangle.normal(this.a,this.b,this.c,optionalTarget)},plane:function(optionalTarget){var result=optionalTarget||new THREE.Plane;return result.setFromCoplanarPoints(this.a,this.b,this.c)},barycoordFromPoint:function(point,optionalTarget){return THREE.Triangle.barycoordFromPoint(point,this.a,this.b,this.c,optionalTarget)},containsPoint:function(point){return THREE.Triangle.containsPoint(point,this.a,this.b,this.c)},equals:function(triangle){return triangle.a.equals(this.a)&&triangle.b.equals(this.b)&&triangle.c.equals(this.c)}},THREE.Channels=function(){this.mask=1},THREE.Channels.prototype={constructor:THREE.Channels,set:function(channel){this.mask=1<i;i++)array[i]=listenerArray[i];for(var i=0;length>i;i++)array[i].call(this,event)}}}},function(THREE){function descSort(a,b){return a.distance-b.distance}function intersectObject(object,raycaster,intersects,recursive){if(object.visible!==!1&&(object.raycast(raycaster,intersects),recursive===!0))for(var children=object.children,i=0,l=children.length;l>i;i++)intersectObject(children[i],raycaster,intersects,!0)}THREE.Raycaster=function(origin,direction,near,far){this.ray=new THREE.Ray(origin,direction),this.near=near||0,this.far=far||1/0,this.params={Mesh:{},Line:{},LOD:{},Points:{threshold:1},Sprite:{}},Object.defineProperties(this.params,{PointCloud:{get:function(){return console.warn("THREE.Raycaster: params.PointCloud has been renamed to params.Points."),this.Points}}})},THREE.Raycaster.prototype={constructor:THREE.Raycaster,linePrecision:1,set:function(origin,direction){this.ray.set(origin,direction)},setFromCamera:function(coords,camera){camera instanceof THREE.PerspectiveCamera?(this.ray.origin.setFromMatrixPosition(camera.matrixWorld),this.ray.direction.set(coords.x,coords.y,.5).unproject(camera).sub(this.ray.origin).normalize()):camera instanceof THREE.OrthographicCamera?(this.ray.origin.set(coords.x,coords.y,-1).unproject(camera),this.ray.direction.set(0,0,-1).transformDirection(camera.matrixWorld)):console.error("THREE.Raycaster: Unsupported camera type.")},intersectObject:function(object,recursive){var intersects=[];return intersectObject(object,this,intersects,recursive),intersects.sort(descSort),intersects},intersectObjects:function(objects,recursive){var intersects=[];if(Array.isArray(objects)===!1)return console.warn("THREE.Raycaster.intersectObjects: objects is not an Array."),intersects;for(var i=0,l=objects.length;l>i;i++)intersectObject(objects[i],this,intersects,recursive);return intersects.sort(descSort),intersects}}}(THREE),THREE.Object3D=function(){function onRotationChange(){quaternion.setFromEuler(rotation,!1)}function onQuaternionChange(){rotation.setFromQuaternion(quaternion,void 0,!1)}Object.defineProperty(this,"id",{value:THREE.Object3DIdCount++}),this.uuid=THREE.Math.generateUUID(),this.name="",this.type="Object3D",this.parent=null,this.channels=new THREE.Channels,this.children=[],this.up=THREE.Object3D.DefaultUp.clone();var position=new THREE.Vector3,rotation=new THREE.Euler,quaternion=new THREE.Quaternion,scale=new THREE.Vector3(1,1,1);rotation.onChange(onRotationChange),quaternion.onChange(onQuaternionChange),Object.defineProperties(this,{position:{enumerable:!0,value:position},rotation:{enumerable:!0,value:rotation},quaternion:{enumerable:!0,value:quaternion},scale:{enumerable:!0,value:scale},modelViewMatrix:{value:new THREE.Matrix4},normalMatrix:{value:new THREE.Matrix3}}),this.rotationAutoUpdate=!0,this.matrix=new THREE.Matrix4,this.matrixWorld=new THREE.Matrix4,this.matrixAutoUpdate=THREE.Object3D.DefaultMatrixAutoUpdate,this.matrixWorldNeedsUpdate=!1,this.visible=!0,this.castShadow=!1,this.receiveShadow=!1,this.frustumCulled=!0,this.renderOrder=0,this.userData={}},THREE.Object3D.DefaultUp=new THREE.Vector3(0,1,0),THREE.Object3D.DefaultMatrixAutoUpdate=!0,THREE.Object3D.prototype={constructor:THREE.Object3D,get eulerOrder(){return console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."),this.rotation.order},set eulerOrder(value){console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."),this.rotation.order=value},get useQuaternion(){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},set useQuaternion(value){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},set renderDepth(value){console.warn("THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.")},applyMatrix:function(matrix){this.matrix.multiplyMatrices(matrix,this.matrix),this.matrix.decompose(this.position,this.quaternion,this.scale)},setRotationFromAxisAngle:function(axis,angle){this.quaternion.setFromAxisAngle(axis,angle)},setRotationFromEuler:function(euler){this.quaternion.setFromEuler(euler,!0)},setRotationFromMatrix:function(m){this.quaternion.setFromRotationMatrix(m)},setRotationFromQuaternion:function(q){this.quaternion.copy(q)},rotateOnAxis:function(){var q1=new THREE.Quaternion;return function(axis,angle){return q1.setFromAxisAngle(axis,angle),this.quaternion.multiply(q1),this}}(),rotateX:function(){var v1=new THREE.Vector3(1,0,0);return function(angle){return this.rotateOnAxis(v1,angle)}}(),rotateY:function(){var v1=new THREE.Vector3(0,1,0);return function(angle){return this.rotateOnAxis(v1,angle)}}(),rotateZ:function(){var v1=new THREE.Vector3(0,0,1);return function(angle){return this.rotateOnAxis(v1,angle)}}(),translateOnAxis:function(){var v1=new THREE.Vector3;return function(axis,distance){return v1.copy(axis).applyQuaternion(this.quaternion),this.position.add(v1.multiplyScalar(distance)),this}}(),translate:function(distance,axis){return console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead."),this.translateOnAxis(axis,distance)},translateX:function(){var v1=new THREE.Vector3(1,0,0);return function(distance){return this.translateOnAxis(v1,distance)}}(),translateY:function(){var v1=new THREE.Vector3(0,1,0);return function(distance){return this.translateOnAxis(v1,distance)}}(),translateZ:function(){var v1=new THREE.Vector3(0,0,1);return function(distance){return this.translateOnAxis(v1,distance)}}(),localToWorld:function(vector){return vector.applyMatrix4(this.matrixWorld)},worldToLocal:function(){var m1=new THREE.Matrix4;return function(vector){return vector.applyMatrix4(m1.getInverse(this.matrixWorld))}}(),lookAt:function(){var m1=new THREE.Matrix4;return function(vector){m1.lookAt(vector,this.position,this.up),this.quaternion.setFromRotationMatrix(m1)}}(),add:function(object){if(arguments.length>1){for(var i=0;i1)for(var i=0;ii;i++){var child=this.children[i],object=child.getObjectByProperty(name,value);if(void 0!==object)return object}},getWorldPosition:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.updateMatrixWorld(!0),result.setFromMatrixPosition(this.matrixWorld)},getWorldQuaternion:function(){var position=new THREE.Vector3,scale=new THREE.Vector3;return function(optionalTarget){var result=optionalTarget||new THREE.Quaternion;return this.updateMatrixWorld(!0),this.matrixWorld.decompose(position,result,scale),result}}(),getWorldRotation:function(){var quaternion=new THREE.Quaternion;return function(optionalTarget){var result=optionalTarget||new THREE.Euler;return this.getWorldQuaternion(quaternion),result.setFromQuaternion(quaternion,this.rotation.order,!1)}}(),getWorldScale:function(){var position=new THREE.Vector3,quaternion=new THREE.Quaternion;return function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.updateMatrixWorld(!0),this.matrixWorld.decompose(position,quaternion,result),result}}(),getWorldDirection:function(){var quaternion=new THREE.Quaternion;return function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.getWorldQuaternion(quaternion),result.set(0,0,1).applyQuaternion(quaternion)}}(),raycast:function(){},traverse:function(callback){callback(this);for(var children=this.children,i=0,l=children.length;l>i;i++)children[i].traverse(callback)},traverseVisible:function(callback){if(this.visible!==!1){callback(this);for(var children=this.children,i=0,l=children.length;l>i;i++)children[i].traverseVisible(callback)}},traverseAncestors:function(callback){var parent=this.parent;null!==parent&&(callback(parent),parent.traverseAncestors(callback))},updateMatrix:function(){this.matrix.compose(this.position,this.quaternion,this.scale),this.matrixWorldNeedsUpdate=!0},updateMatrixWorld:function(force){this.matrixAutoUpdate===!0&&this.updateMatrix(),this.matrixWorldNeedsUpdate!==!0&&force!==!0||(null===this.parent?this.matrixWorld.copy(this.matrix):this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix),this.matrixWorldNeedsUpdate=!1,force=!0);for(var i=0,l=this.children.length;l>i;i++)this.children[i].updateMatrixWorld(force)},toJSON:function(meta){function extractFromCache(cache){var values=[];for(var key in cache){var data=cache[key];delete data.metadata,values.push(data)}return values}var isRootObject=void 0===meta,output={};isRootObject&&(meta={geometries:{},materials:{},textures:{},images:{}},output.metadata={version:4.4,type:"Object",generator:"Object3D.toJSON"});var object={};if(object.uuid=this.uuid,object.type=this.type,""!==this.name&&(object.name=this.name),"{}"!==JSON.stringify(this.userData)&&(object.userData=this.userData),this.castShadow===!0&&(object.castShadow=!0),this.receiveShadow===!0&&(object.receiveShadow=!0),this.visible===!1&&(object.visible=!1),object.matrix=this.matrix.toArray(),void 0!==this.geometry&&(void 0===meta.geometries[this.geometry.uuid]&&(meta.geometries[this.geometry.uuid]=this.geometry.toJSON(meta)),object.geometry=this.geometry.uuid),void 0!==this.material&&(void 0===meta.materials[this.material.uuid]&&(meta.materials[this.material.uuid]=this.material.toJSON(meta)),object.material=this.material.uuid),this.children.length>0){object.children=[];for(var i=0;i0&&(output.geometries=geometries),materials.length>0&&(output.materials=materials),textures.length>0&&(output.textures=textures),images.length>0&&(output.images=images)}return output.object=object,output},clone:function(recursive){return(new this.constructor).copy(this,recursive)},copy:function(source,recursive){if(void 0===recursive&&(recursive=!0),this.name=source.name,this.up.copy(source.up),this.position.copy(source.position),this.quaternion.copy(source.quaternion),this.scale.copy(source.scale),this.rotationAutoUpdate=source.rotationAutoUpdate,this.matrix.copy(source.matrix),this.matrixWorld.copy(source.matrixWorld),this.matrixAutoUpdate=source.matrixAutoUpdate,this.matrixWorldNeedsUpdate=source.matrixWorldNeedsUpdate,this.visible=source.visible,this.castShadow=source.castShadow,this.receiveShadow=source.receiveShadow,this.frustumCulled=source.frustumCulled,this.renderOrder=source.renderOrder,this.userData=JSON.parse(JSON.stringify(source.userData)),recursive===!0)for(var i=0;ii;i++)this.vertexNormals[i]=source.vertexNormals[i].clone();for(var i=0,il=source.vertexColors.length;il>i;i++)this.vertexColors[i]=source.vertexColors[i].clone();return this}},THREE.Face4=function(a,b,c,d,normal,color,materialIndex){return console.warn("THREE.Face4 has been removed. A THREE.Face3 will be created instead."),new THREE.Face3(a,b,c,normal,color,materialIndex)},THREE.BufferAttribute=function(array,itemSize){this.uuid=THREE.Math.generateUUID(),this.array=array,this.itemSize=itemSize,this.dynamic=!1,this.updateRange={offset:0,count:-1},this.version=0},THREE.BufferAttribute.prototype={constructor:THREE.BufferAttribute,get length(){return console.warn("THREE.BufferAttribute: .length has been deprecated. Please use .count."),this.array.length},get count(){return this.array.length/this.itemSize},set needsUpdate(value){value===!0&&this.version++},setDynamic:function(value){return this.dynamic=value,this},copy:function(source){return this.array=new source.array.constructor(source.array),this.itemSize=source.itemSize,this.dynamic=source.dynamic,this},copyAt:function(index1,attribute,index2){index1*=this.itemSize,index2*=attribute.itemSize;for(var i=0,l=this.itemSize;l>i;i++)this.array[index1+i]=attribute.array[index2+i];return this},copyArray:function(array){return this.array.set(array),this},copyColorsArray:function(colors){for(var array=this.array,offset=0,i=0,l=colors.length;l>i;i++){var color=colors[i];void 0===color&&(console.warn("THREE.BufferAttribute.copyColorsArray(): color is undefined",i),color=new THREE.Color),array[offset++]=color.r,array[offset++]=color.g,array[offset++]=color.b}return this},copyIndicesArray:function(indices){for(var array=this.array,offset=0,i=0,l=indices.length;l>i;i++){var index=indices[i];array[offset++]=index.a,array[offset++]=index.b,array[offset++]=index.c}return this},copyVector2sArray:function(vectors){for(var array=this.array,offset=0,i=0,l=vectors.length;l>i;i++){var vector=vectors[i];void 0===vector&&(console.warn("THREE.BufferAttribute.copyVector2sArray(): vector is undefined",i),vector=new THREE.Vector2),array[offset++]=vector.x,array[offset++]=vector.y}return this},copyVector3sArray:function(vectors){for(var array=this.array,offset=0,i=0,l=vectors.length;l>i;i++){var vector=vectors[i];void 0===vector&&(console.warn("THREE.BufferAttribute.copyVector3sArray(): vector is undefined",i),vector=new THREE.Vector3),array[offset++]=vector.x,array[offset++]=vector.y,array[offset++]=vector.z}return this},copyVector4sArray:function(vectors){for(var array=this.array,offset=0,i=0,l=vectors.length;l>i;i++){var vector=vectors[i];void 0===vector&&(console.warn("THREE.BufferAttribute.copyVector4sArray(): vector is undefined",i),vector=new THREE.Vector4),array[offset++]=vector.x,array[offset++]=vector.y,array[offset++]=vector.z,array[offset++]=vector.w}return this},set:function(value,offset){return void 0===offset&&(offset=0),this.array.set(value,offset),this},getX:function(index){return this.array[index*this.itemSize]},setX:function(index,x){return this.array[index*this.itemSize]=x,this},getY:function(index){return this.array[index*this.itemSize+1]},setY:function(index,y){return this.array[index*this.itemSize+1]=y,this},getZ:function(index){return this.array[index*this.itemSize+2]},setZ:function(index,z){return this.array[index*this.itemSize+2]=z,this},getW:function(index){return this.array[index*this.itemSize+3]},setW:function(index,w){return this.array[index*this.itemSize+3]=w,this},setXY:function(index,x,y){return index*=this.itemSize,this.array[index+0]=x,this.array[index+1]=y,this},setXYZ:function(index,x,y,z){return index*=this.itemSize,this.array[index+0]=x,this.array[index+1]=y,this.array[index+2]=z,this},setXYZW:function(index,x,y,z,w){return index*=this.itemSize,this.array[index+0]=x,this.array[index+1]=y,this.array[index+2]=z,this.array[index+3]=w,this},clone:function(){return(new this.constructor).copy(this)}},THREE.Int8Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Int8Array(array),itemSize)},THREE.Uint8Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint8Array(array),itemSize)},THREE.Uint8ClampedAttribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint8ClampedArray(array),itemSize)},THREE.Int16Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Int16Array(array),itemSize)},THREE.Uint16Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint16Array(array),itemSize)},THREE.Int32Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Int32Array(array),itemSize)},THREE.Uint32Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint32Array(array),itemSize)},THREE.Float32Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Float32Array(array),itemSize)},THREE.Float64Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Float64Array(array),itemSize)},THREE.DynamicBufferAttribute=function(array,itemSize){return console.warn("THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead."),new THREE.BufferAttribute(array,itemSize).setDynamic(!0)},THREE.InstancedBufferAttribute=function(array,itemSize,meshPerAttribute){THREE.BufferAttribute.call(this,array,itemSize),this.meshPerAttribute=meshPerAttribute||1},THREE.InstancedBufferAttribute.prototype=Object.create(THREE.BufferAttribute.prototype),THREE.InstancedBufferAttribute.prototype.constructor=THREE.InstancedBufferAttribute,THREE.InstancedBufferAttribute.prototype.copy=function(source){return THREE.BufferAttribute.prototype.copy.call(this,source),this.meshPerAttribute=source.meshPerAttribute,this},THREE.InterleavedBuffer=function(array,stride){this.uuid=THREE.Math.generateUUID(),this.array=array,this.stride=stride,this.dynamic=!1,this.updateRange={offset:0,count:-1},this.version=0},THREE.InterleavedBuffer.prototype={constructor:THREE.InterleavedBuffer,get length(){return this.array.length},get count(){return this.array.length/this.stride},set needsUpdate(value){value===!0&&this.version++},setDynamic:function(value){return this.dynamic=value,this},copy:function(source){this.array=new source.array.constructor(source.array),this.stride=source.stride,this.dynamic=source.dynamic},copyAt:function(index1,attribute,index2){index1*=this.stride,index2*=attribute.stride;for(var i=0,l=this.stride;l>i;i++)this.array[index1+i]=attribute.array[index2+i];return this},set:function(value,offset){return void 0===offset&&(offset=0),this.array.set(value,offset),this},clone:function(){return(new this.constructor).copy(this)}},THREE.InstancedInterleavedBuffer=function(array,stride,meshPerAttribute){THREE.InterleavedBuffer.call(this,array,stride),this.meshPerAttribute=meshPerAttribute||1},THREE.InstancedInterleavedBuffer.prototype=Object.create(THREE.InterleavedBuffer.prototype),THREE.InstancedInterleavedBuffer.prototype.constructor=THREE.InstancedInterleavedBuffer,THREE.InstancedInterleavedBuffer.prototype.copy=function(source){return THREE.InterleavedBuffer.prototype.copy.call(this,source),this.meshPerAttribute=source.meshPerAttribute,this},THREE.InterleavedBufferAttribute=function(interleavedBuffer,itemSize,offset){this.uuid=THREE.Math.generateUUID(),this.data=interleavedBuffer,this.itemSize=itemSize,this.offset=offset},THREE.InterleavedBufferAttribute.prototype={constructor:THREE.InterleavedBufferAttribute,get length(){return console.warn("THREE.BufferAttribute: .length has been deprecated. Please use .count."),this.array.length},get count(){return this.data.array.length/this.data.stride},setX:function(index,x){return this.data.array[index*this.data.stride+this.offset]=x,this},setY:function(index,y){return this.data.array[index*this.data.stride+this.offset+1]=y,this},setZ:function(index,z){return this.data.array[index*this.data.stride+this.offset+2]=z,this},setW:function(index,w){return this.data.array[index*this.data.stride+this.offset+3]=w,this},getX:function(index){return this.data.array[index*this.data.stride+this.offset]},getY:function(index){return this.data.array[index*this.data.stride+this.offset+1]},getZ:function(index){return this.data.array[index*this.data.stride+this.offset+2]},getW:function(index){return this.data.array[index*this.data.stride+this.offset+3]},setXY:function(index,x,y){return index=index*this.data.stride+this.offset,this.data.array[index+0]=x,this.data.array[index+1]=y,this},setXYZ:function(index,x,y,z){return index=index*this.data.stride+this.offset,this.data.array[index+0]=x,this.data.array[index+1]=y,this.data.array[index+2]=z,this},setXYZW:function(index,x,y,z,w){return index=index*this.data.stride+this.offset,this.data.array[index+0]=x,this.data.array[index+1]=y,this.data.array[index+2]=z,this.data.array[index+3]=w,this}},THREE.Geometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++}),this.uuid=THREE.Math.generateUUID(),this.name="",this.type="Geometry",this.vertices=[],this.colors=[],this.faces=[],this.faceVertexUvs=[[]],this.morphTargets=[],this.morphNormals=[],this.skinWeights=[],this.skinIndices=[],this.lineDistances=[],this.boundingBox=null,this.boundingSphere=null,this.verticesNeedUpdate=!1,this.elementsNeedUpdate=!1,this.uvsNeedUpdate=!1,this.normalsNeedUpdate=!1,this.colorsNeedUpdate=!1,this.lineDistancesNeedUpdate=!1,this.groupsNeedUpdate=!1},THREE.Geometry.prototype={constructor:THREE.Geometry,applyMatrix:function(matrix){for(var normalMatrix=(new THREE.Matrix3).getNormalMatrix(matrix),i=0,il=this.vertices.length;il>i;i++){var vertex=this.vertices[i];vertex.applyMatrix4(matrix)}for(var i=0,il=this.faces.length;il>i;i++){var face=this.faces[i];face.normal.applyMatrix3(normalMatrix).normalize();for(var j=0,jl=face.vertexNormals.length;jl>j;j++)face.vertexNormals[j].applyMatrix3(normalMatrix).normalize();
+}null!==this.boundingBox&&this.computeBoundingBox(),null!==this.boundingSphere&&this.computeBoundingSphere(),this.verticesNeedUpdate=!0,this.normalsNeedUpdate=!0},rotateX:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationX(angle),this.applyMatrix(m1),this}}(),rotateY:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationY(angle),this.applyMatrix(m1),this}}(),rotateZ:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationZ(angle),this.applyMatrix(m1),this}}(),translate:function(){var m1;return function(x,y,z){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeTranslation(x,y,z),this.applyMatrix(m1),this}}(),scale:function(){var m1;return function(x,y,z){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeScale(x,y,z),this.applyMatrix(m1),this}}(),lookAt:function(){var obj;return function(vector){void 0===obj&&(obj=new THREE.Object3D),obj.lookAt(vector),obj.updateMatrix(),this.applyMatrix(obj.matrix)}}(),fromBufferGeometry:function(geometry){function addFace(a,b,c){var vertexNormals=void 0!==normals?[tempNormals[a].clone(),tempNormals[b].clone(),tempNormals[c].clone()]:[],vertexColors=void 0!==colors?[scope.colors[a].clone(),scope.colors[b].clone(),scope.colors[c].clone()]:[],face=new THREE.Face3(a,b,c,vertexNormals,vertexColors);scope.faces.push(face),void 0!==uvs&&scope.faceVertexUvs[0].push([tempUVs[a].clone(),tempUVs[b].clone(),tempUVs[c].clone()]),void 0!==uvs2&&scope.faceVertexUvs[1].push([tempUVs2[a].clone(),tempUVs2[b].clone(),tempUVs2[c].clone()])}var scope=this,indices=null!==geometry.index?geometry.index.array:void 0,attributes=geometry.attributes,vertices=attributes.position.array,normals=void 0!==attributes.normal?attributes.normal.array:void 0,colors=void 0!==attributes.color?attributes.color.array:void 0,uvs=void 0!==attributes.uv?attributes.uv.array:void 0,uvs2=void 0!==attributes.uv2?attributes.uv2.array:void 0;void 0!==uvs2&&(this.faceVertexUvs[1]=[]);for(var tempNormals=[],tempUVs=[],tempUVs2=[],i=0,j=0,k=0;i0)for(var i=0;ij;j+=3)addFace(indices[j],indices[j+1],indices[j+2]);else for(var i=0;if;f++){var face=this.faces[f],vA=this.vertices[face.a],vB=this.vertices[face.b],vC=this.vertices[face.c];cb.subVectors(vC,vB),ab.subVectors(vA,vB),cb.cross(ab),cb.normalize(),face.normal.copy(cb)}},computeVertexNormals:function(areaWeighted){var v,vl,f,fl,face,vertices;for(vertices=new Array(this.vertices.length),v=0,vl=this.vertices.length;vl>v;v++)vertices[v]=new THREE.Vector3;if(areaWeighted){var vA,vB,vC,cb=new THREE.Vector3,ab=new THREE.Vector3;for(f=0,fl=this.faces.length;fl>f;f++)face=this.faces[f],vA=this.vertices[face.a],vB=this.vertices[face.b],vC=this.vertices[face.c],cb.subVectors(vC,vB),ab.subVectors(vA,vB),cb.cross(ab),vertices[face.a].add(cb),vertices[face.b].add(cb),vertices[face.c].add(cb)}else for(f=0,fl=this.faces.length;fl>f;f++)face=this.faces[f],vertices[face.a].add(face.normal),vertices[face.b].add(face.normal),vertices[face.c].add(face.normal);for(v=0,vl=this.vertices.length;vl>v;v++)vertices[v].normalize();for(f=0,fl=this.faces.length;fl>f;f++){face=this.faces[f];var vertexNormals=face.vertexNormals;3===vertexNormals.length?(vertexNormals[0].copy(vertices[face.a]),vertexNormals[1].copy(vertices[face.b]),vertexNormals[2].copy(vertices[face.c])):(vertexNormals[0]=vertices[face.a].clone(),vertexNormals[1]=vertices[face.b].clone(),vertexNormals[2]=vertices[face.c].clone())}},computeMorphNormals:function(){var i,il,f,fl,face;for(f=0,fl=this.faces.length;fl>f;f++)for(face=this.faces[f],face.__originalFaceNormal?face.__originalFaceNormal.copy(face.normal):face.__originalFaceNormal=face.normal.clone(),face.__originalVertexNormals||(face.__originalVertexNormals=[]),i=0,il=face.vertexNormals.length;il>i;i++)face.__originalVertexNormals[i]?face.__originalVertexNormals[i].copy(face.vertexNormals[i]):face.__originalVertexNormals[i]=face.vertexNormals[i].clone();var tmpGeo=new THREE.Geometry;for(tmpGeo.faces=this.faces,i=0,il=this.morphTargets.length;il>i;i++){if(!this.morphNormals[i]){this.morphNormals[i]={},this.morphNormals[i].faceNormals=[],this.morphNormals[i].vertexNormals=[];var faceNormal,vertexNormals,dstNormalsFace=this.morphNormals[i].faceNormals,dstNormalsVertex=this.morphNormals[i].vertexNormals;for(f=0,fl=this.faces.length;fl>f;f++)faceNormal=new THREE.Vector3,vertexNormals={a:new THREE.Vector3,b:new THREE.Vector3,c:new THREE.Vector3},dstNormalsFace.push(faceNormal),dstNormalsVertex.push(vertexNormals)}var morphNormals=this.morphNormals[i];tmpGeo.vertices=this.morphTargets[i].vertices,tmpGeo.computeFaceNormals(),tmpGeo.computeVertexNormals();var faceNormal,vertexNormals;for(f=0,fl=this.faces.length;fl>f;f++)face=this.faces[f],faceNormal=morphNormals.faceNormals[f],vertexNormals=morphNormals.vertexNormals[f],faceNormal.copy(face.normal),vertexNormals.a.copy(face.vertexNormals[0]),vertexNormals.b.copy(face.vertexNormals[1]),vertexNormals.c.copy(face.vertexNormals[2])}for(f=0,fl=this.faces.length;fl>f;f++)face=this.faces[f],face.normal=face.__originalFaceNormal,face.vertexNormals=face.__originalVertexNormals},computeTangents:function(){console.warn("THREE.Geometry: .computeTangents() has been removed.")},computeLineDistances:function(){for(var d=0,vertices=this.vertices,i=0,il=vertices.length;il>i;i++)i>0&&(d+=vertices[i].distanceTo(vertices[i-1])),this.lineDistances[i]=d},computeBoundingBox:function(){null===this.boundingBox&&(this.boundingBox=new THREE.Box3),this.boundingBox.setFromPoints(this.vertices)},computeBoundingSphere:function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere),this.boundingSphere.setFromPoints(this.vertices)},merge:function(geometry,matrix,materialIndexOffset){if(geometry instanceof THREE.Geometry==!1)return void console.error("THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.",geometry);var normalMatrix,vertexOffset=this.vertices.length,vertices1=this.vertices,vertices2=geometry.vertices,faces1=this.faces,faces2=geometry.faces,uvs1=this.faceVertexUvs[0],uvs2=geometry.faceVertexUvs[0];void 0===materialIndexOffset&&(materialIndexOffset=0),void 0!==matrix&&(normalMatrix=(new THREE.Matrix3).getNormalMatrix(matrix));for(var i=0,il=vertices2.length;il>i;i++){var vertex=vertices2[i],vertexCopy=vertex.clone();void 0!==matrix&&vertexCopy.applyMatrix4(matrix),vertices1.push(vertexCopy)}for(i=0,il=faces2.length;il>i;i++){var faceCopy,normal,color,face=faces2[i],faceVertexNormals=face.vertexNormals,faceVertexColors=face.vertexColors;faceCopy=new THREE.Face3(face.a+vertexOffset,face.b+vertexOffset,face.c+vertexOffset),faceCopy.normal.copy(face.normal),void 0!==normalMatrix&&faceCopy.normal.applyMatrix3(normalMatrix).normalize();for(var j=0,jl=faceVertexNormals.length;jl>j;j++)normal=faceVertexNormals[j].clone(),void 0!==normalMatrix&&normal.applyMatrix3(normalMatrix).normalize(),faceCopy.vertexNormals.push(normal);faceCopy.color.copy(face.color);for(var j=0,jl=faceVertexColors.length;jl>j;j++)color=faceVertexColors[j],faceCopy.vertexColors.push(color.clone());faceCopy.materialIndex=face.materialIndex+materialIndexOffset,faces1.push(faceCopy)}for(i=0,il=uvs2.length;il>i;i++){var uv=uvs2[i],uvCopy=[];if(void 0!==uv){for(var j=0,jl=uv.length;jl>j;j++)uvCopy.push(uv[j].clone());uvs1.push(uvCopy)}}},mergeMesh:function(mesh){return mesh instanceof THREE.Mesh==!1?void console.error("THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.",mesh):(mesh.matrixAutoUpdate&&mesh.updateMatrix(),void this.merge(mesh.geometry,mesh.matrix))},mergeVertices:function(){var v,key,i,il,face,indices,j,jl,verticesMap={},unique=[],changes=[],precisionPoints=4,precision=Math.pow(10,precisionPoints);for(i=0,il=this.vertices.length;il>i;i++)v=this.vertices[i],key=Math.round(v.x*precision)+"_"+Math.round(v.y*precision)+"_"+Math.round(v.z*precision),void 0===verticesMap[key]?(verticesMap[key]=i,unique.push(this.vertices[i]),changes[i]=unique.length-1):changes[i]=changes[verticesMap[key]];var faceIndicesToRemove=[];for(i=0,il=this.faces.length;il>i;i++){face=this.faces[i],face.a=changes[face.a],face.b=changes[face.b],face.c=changes[face.c],indices=[face.a,face.b,face.c];for(var dupIndex=-1,n=0;3>n;n++)if(indices[n]===indices[(n+1)%3]){dupIndex=n,faceIndicesToRemove.push(i);break}}for(i=faceIndicesToRemove.length-1;i>=0;i--){var idx=faceIndicesToRemove[i];for(this.faces.splice(idx,1),j=0,jl=this.faceVertexUvs.length;jl>j;j++)this.faceVertexUvs[j].splice(idx,1)}var diff=this.vertices.length-unique.length;return this.vertices=unique,diff},sortFacesByMaterialIndex:function(){function materialIndexSort(a,b){return a.materialIndex-b.materialIndex}for(var faces=this.faces,length=faces.length,i=0;length>i;i++)faces[i]._id=i;faces.sort(materialIndexSort);var newUvs1,newUvs2,uvs1=this.faceVertexUvs[0],uvs2=this.faceVertexUvs[1];uvs1&&uvs1.length===length&&(newUvs1=[]),uvs2&&uvs2.length===length&&(newUvs2=[]);for(var i=0;length>i;i++){var id=faces[i]._id;newUvs1&&newUvs1.push(uvs1[id]),newUvs2&&newUvs2.push(uvs2[id])}newUvs1&&(this.faceVertexUvs[0]=newUvs1),newUvs2&&(this.faceVertexUvs[1]=newUvs2)},toJSON:function(){function setBit(value,position,enabled){return enabled?value|1<0,hasFaceVertexNormal=face.vertexNormals.length>0,hasFaceColor=1!==face.color.r||1!==face.color.g||1!==face.color.b,hasFaceVertexColor=face.vertexColors.length>0,faceType=0;if(faceType=setBit(faceType,0,0),faceType=setBit(faceType,1,hasMaterial),faceType=setBit(faceType,2,hasFaceUv),faceType=setBit(faceType,3,hasFaceVertexUv),faceType=setBit(faceType,4,hasFaceNormal),faceType=setBit(faceType,5,hasFaceVertexNormal),faceType=setBit(faceType,6,hasFaceColor),faceType=setBit(faceType,7,hasFaceVertexColor),faces.push(faceType),faces.push(face.a,face.b,face.c),hasFaceVertexUv){var faceVertexUvs=this.faceVertexUvs[0][i];faces.push(getUvIndex(faceVertexUvs[0]),getUvIndex(faceVertexUvs[1]),getUvIndex(faceVertexUvs[2]))}if(hasFaceNormal&&faces.push(getNormalIndex(face.normal)),hasFaceVertexNormal){var vertexNormals=face.vertexNormals;faces.push(getNormalIndex(vertexNormals[0]),getNormalIndex(vertexNormals[1]),getNormalIndex(vertexNormals[2]))}if(hasFaceColor&&faces.push(getColorIndex(face.color)),hasFaceVertexColor){var vertexColors=face.vertexColors;faces.push(getColorIndex(vertexColors[0]),getColorIndex(vertexColors[1]),getColorIndex(vertexColors[2]))}}return data.data={},data.data.vertices=vertices,data.data.normals=normals,colors.length>0&&(data.data.colors=colors),uvs.length>0&&(data.data.uvs=[uvs]),data.data.faces=faces,data},clone:function(){return(new this.constructor).copy(this)},copy:function(source){this.vertices=[],this.faces=[],this.faceVertexUvs=[[]];for(var vertices=source.vertices,i=0,il=vertices.length;il>i;i++)this.vertices.push(vertices[i].clone());for(var faces=source.faces,i=0,il=faces.length;il>i;i++)this.faces.push(faces[i].clone());for(var i=0,il=source.faceVertexUvs.length;il>i;i++){var faceVertexUvs=source.faceVertexUvs[i];void 0===this.faceVertexUvs[i]&&(this.faceVertexUvs[i]=[]);for(var j=0,jl=faceVertexUvs.length;jl>j;j++){for(var uvs=faceVertexUvs[j],uvsCopy=[],k=0,kl=uvs.length;kl>k;k++){var uv=uvs[k];uvsCopy.push(uv.clone())}this.faceVertexUvs[i].push(uvsCopy)}}return this},dispose:function(){this.dispatchEvent({type:"dispose"})}},THREE.EventDispatcher.prototype.apply(THREE.Geometry.prototype),THREE.GeometryIdCount=0,THREE.DirectGeometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++}),this.uuid=THREE.Math.generateUUID(),this.name="",this.type="DirectGeometry",this.indices=[],this.vertices=[],this.normals=[],this.colors=[],this.uvs=[],this.uvs2=[],this.groups=[],this.morphTargets={},this.skinWeights=[],this.skinIndices=[],this.boundingBox=null,this.boundingSphere=null,this.verticesNeedUpdate=!1,this.normalsNeedUpdate=!1,this.colorsNeedUpdate=!1,this.uvsNeedUpdate=!1,this.groupsNeedUpdate=!1},THREE.DirectGeometry.prototype={constructor:THREE.DirectGeometry,computeBoundingBox:THREE.Geometry.prototype.computeBoundingBox,computeBoundingSphere:THREE.Geometry.prototype.computeBoundingSphere,computeFaceNormals:function(){console.warn("THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.")},computeVertexNormals:function(){console.warn("THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.")},computeGroups:function(geometry){for(var group,materialIndex,groups=[],faces=geometry.faces,i=0;i0,hasFaceVertexUv2=faceVertexUvs[1]&&faceVertexUvs[1].length>0,morphTargets=geometry.morphTargets,morphTargetsLength=morphTargets.length;if(morphTargetsLength>0){for(var morphTargetsPosition=[],i=0;morphTargetsLength>i;i++)morphTargetsPosition[i]=[];this.morphTargets.position=morphTargetsPosition}var morphNormals=geometry.morphNormals,morphNormalsLength=morphNormals.length;if(morphNormalsLength>0){for(var morphTargetsNormal=[],i=0;morphNormalsLength>i;i++)morphTargetsNormal[i]=[];this.morphTargets.normal=morphTargetsNormal}for(var skinIndices=geometry.skinIndices,skinWeights=geometry.skinWeights,hasSkinIndices=skinIndices.length===vertices.length,hasSkinWeights=skinWeights.length===vertices.length,i=0;ij;j++){var morphTarget=morphTargets[j].vertices;morphTargetsPosition[j].push(morphTarget[face.a],morphTarget[face.b],morphTarget[face.c])}for(var j=0;morphNormalsLength>j;j++){var morphNormal=morphNormals[j].vertexNormals[i];morphTargetsNormal[j].push(morphNormal.a,morphNormal.b,morphNormal.c)}hasSkinIndices&&this.skinIndices.push(skinIndices[face.a],skinIndices[face.b],skinIndices[face.c]),hasSkinWeights&&this.skinWeights.push(skinWeights[face.a],skinWeights[face.b],skinWeights[face.c])}return this.computeGroups(geometry),this.verticesNeedUpdate=geometry.verticesNeedUpdate,this.normalsNeedUpdate=geometry.normalsNeedUpdate,this.colorsNeedUpdate=geometry.colorsNeedUpdate,this.uvsNeedUpdate=geometry.uvsNeedUpdate,this.groupsNeedUpdate=geometry.groupsNeedUpdate,this},dispose:function(){this.dispatchEvent({type:"dispose"})}},THREE.EventDispatcher.prototype.apply(THREE.DirectGeometry.prototype),THREE.BufferGeometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++}),this.uuid=THREE.Math.generateUUID(),this.name="",this.type="BufferGeometry",this.index=null,this.attributes={},this.morphAttributes={},this.groups=[],this.boundingBox=null,this.boundingSphere=null,this.drawRange={start:0,count:1/0}},THREE.BufferGeometry.prototype={constructor:THREE.BufferGeometry,addIndex:function(index){console.warn("THREE.BufferGeometry: .addIndex() has been renamed to .setIndex()."),this.setIndex(index)},getIndex:function(){return this.index},setIndex:function(index){this.index=index},addAttribute:function(name,attribute){return attribute instanceof THREE.BufferAttribute==!1&&attribute instanceof THREE.InterleavedBufferAttribute==!1?(console.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )."),void this.addAttribute(name,new THREE.BufferAttribute(arguments[1],arguments[2]))):"index"===name?(console.warn("THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute."),void this.setIndex(attribute)):void(this.attributes[name]=attribute)},getAttribute:function(name){return this.attributes[name]},removeAttribute:function(name){delete this.attributes[name]},get drawcalls(){return console.error("THREE.BufferGeometry: .drawcalls has been renamed to .groups."),this.groups},get offsets(){return console.warn("THREE.BufferGeometry: .offsets has been renamed to .groups."),this.groups},addDrawCall:function(start,count,indexOffset){void 0!==indexOffset&&console.warn("THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset."),console.warn("THREE.BufferGeometry: .addDrawCall() is now .addGroup()."),this.addGroup(start,count)},clearDrawCalls:function(){console.warn("THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups()."),this.clearGroups()},addGroup:function(start,count,materialIndex){this.groups.push({start:start,count:count,materialIndex:void 0!==materialIndex?materialIndex:0})},clearGroups:function(){this.groups=[]},setDrawRange:function(start,count){this.drawRange.start=start,this.drawRange.count=count},applyMatrix:function(matrix){var position=this.attributes.position;void 0!==position&&(matrix.applyToVector3Array(position.array),position.needsUpdate=!0);var normal=this.attributes.normal;if(void 0!==normal){var normalMatrix=(new THREE.Matrix3).getNormalMatrix(matrix);normalMatrix.applyToVector3Array(normal.array),normal.needsUpdate=!0}null!==this.boundingBox&&this.computeBoundingBox(),null!==this.boundingSphere&&this.computeBoundingSphere()},rotateX:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationX(angle),this.applyMatrix(m1),this}}(),rotateY:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationY(angle),this.applyMatrix(m1),this}}(),rotateZ:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationZ(angle),this.applyMatrix(m1),this}}(),translate:function(){var m1;return function(x,y,z){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeTranslation(x,y,z),this.applyMatrix(m1),this}}(),scale:function(){var m1;return function(x,y,z){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeScale(x,y,z),this.applyMatrix(m1),this}}(),lookAt:function(){var obj;return function(vector){void 0===obj&&(obj=new THREE.Object3D),obj.lookAt(vector),obj.updateMatrix(),this.applyMatrix(obj.matrix)}}(),center:function(){this.computeBoundingBox();var offset=this.boundingBox.center().negate();return this.translate(offset.x,offset.y,offset.z),offset},setFromObject:function(object){var geometry=object.geometry;if(object instanceof THREE.Points||object instanceof THREE.Line){var positions=new THREE.Float32Attribute(3*geometry.vertices.length,3),colors=new THREE.Float32Attribute(3*geometry.colors.length,3);if(this.addAttribute("position",positions.copyVector3sArray(geometry.vertices)),this.addAttribute("color",colors.copyColorsArray(geometry.colors)),geometry.lineDistances&&geometry.lineDistances.length===geometry.vertices.length){var lineDistances=new THREE.Float32Attribute(geometry.lineDistances.length,1);this.addAttribute("lineDistance",lineDistances.copyArray(geometry.lineDistances))}null!==geometry.boundingSphere&&(this.boundingSphere=geometry.boundingSphere.clone()),null!==geometry.boundingBox&&(this.boundingBox=geometry.boundingBox.clone())}else object instanceof THREE.Mesh&&geometry instanceof THREE.Geometry&&this.fromGeometry(geometry);return this},updateFromObject:function(object){var geometry=object.geometry;if(object instanceof THREE.Mesh){var direct=geometry.__directGeometry;if(void 0===direct)return this.fromGeometry(geometry);direct.verticesNeedUpdate=geometry.verticesNeedUpdate,direct.normalsNeedUpdate=geometry.normalsNeedUpdate,direct.colorsNeedUpdate=geometry.colorsNeedUpdate,direct.uvsNeedUpdate=geometry.uvsNeedUpdate,direct.groupsNeedUpdate=geometry.groupsNeedUpdate,geometry.verticesNeedUpdate=!1,geometry.normalsNeedUpdate=!1,geometry.colorsNeedUpdate=!1,geometry.uvsNeedUpdate=!1,geometry.groupsNeedUpdate=!1,geometry=direct}if(geometry.verticesNeedUpdate===!0){var attribute=this.attributes.position;void 0!==attribute&&(attribute.copyVector3sArray(geometry.vertices),attribute.needsUpdate=!0),geometry.verticesNeedUpdate=!1}if(geometry.normalsNeedUpdate===!0){var attribute=this.attributes.normal;void 0!==attribute&&(attribute.copyVector3sArray(geometry.normals),attribute.needsUpdate=!0),geometry.normalsNeedUpdate=!1}if(geometry.colorsNeedUpdate===!0){var attribute=this.attributes.color;void 0!==attribute&&(attribute.copyColorsArray(geometry.colors),attribute.needsUpdate=!0),geometry.colorsNeedUpdate=!1}if(geometry.uvsNeedUpdate){var attribute=this.attributes.uv;void 0!==attribute&&(attribute.copyVector2sArray(geometry.uvs),attribute.needsUpdate=!0),geometry.uvsNeedUpdate=!1}if(geometry.lineDistancesNeedUpdate){var attribute=this.attributes.lineDistance;void 0!==attribute&&(attribute.copyArray(geometry.lineDistances),attribute.needsUpdate=!0),geometry.lineDistancesNeedUpdate=!1}return geometry.groupsNeedUpdate&&(geometry.computeGroups(object.geometry),this.groups=geometry.groups,geometry.groupsNeedUpdate=!1),this},fromGeometry:function(geometry){return geometry.__directGeometry=(new THREE.DirectGeometry).fromGeometry(geometry),this.fromDirectGeometry(geometry.__directGeometry)},fromDirectGeometry:function(geometry){var positions=new Float32Array(3*geometry.vertices.length);if(this.addAttribute("position",new THREE.BufferAttribute(positions,3).copyVector3sArray(geometry.vertices)),geometry.normals.length>0){var normals=new Float32Array(3*geometry.normals.length);this.addAttribute("normal",new THREE.BufferAttribute(normals,3).copyVector3sArray(geometry.normals))}if(geometry.colors.length>0){var colors=new Float32Array(3*geometry.colors.length);this.addAttribute("color",new THREE.BufferAttribute(colors,3).copyColorsArray(geometry.colors))}if(geometry.uvs.length>0){var uvs=new Float32Array(2*geometry.uvs.length);this.addAttribute("uv",new THREE.BufferAttribute(uvs,2).copyVector2sArray(geometry.uvs))}if(geometry.uvs2.length>0){var uvs2=new Float32Array(2*geometry.uvs2.length);this.addAttribute("uv2",new THREE.BufferAttribute(uvs2,2).copyVector2sArray(geometry.uvs2))}if(geometry.indices.length>0){var TypeArray=geometry.vertices.length>65535?Uint32Array:Uint16Array,indices=new TypeArray(3*geometry.indices.length);this.setIndex(new THREE.BufferAttribute(indices,1).copyIndicesArray(geometry.indices))}this.groups=geometry.groups;for(var name in geometry.morphTargets){for(var array=[],morphTargets=geometry.morphTargets[name],i=0,l=morphTargets.length;l>i;i++){var morphTarget=morphTargets[i],attribute=new THREE.Float32Attribute(3*morphTarget.length,3);array.push(attribute.copyVector3sArray(morphTarget))}this.morphAttributes[name]=array}if(geometry.skinIndices.length>0){var skinIndices=new THREE.Float32Attribute(4*geometry.skinIndices.length,4);this.addAttribute("skinIndex",skinIndices.copyVector4sArray(geometry.skinIndices))}if(geometry.skinWeights.length>0){var skinWeights=new THREE.Float32Attribute(4*geometry.skinWeights.length,4);this.addAttribute("skinWeight",skinWeights.copyVector4sArray(geometry.skinWeights))}return null!==geometry.boundingSphere&&(this.boundingSphere=geometry.boundingSphere.clone()),null!==geometry.boundingBox&&(this.boundingBox=geometry.boundingBox.clone()),this},computeBoundingBox:function(){var vector=new THREE.Vector3;return function(){null===this.boundingBox&&(this.boundingBox=new THREE.Box3);var positions=this.attributes.position.array;if(positions){var bb=this.boundingBox;bb.makeEmpty();for(var i=0,il=positions.length;il>i;i+=3)vector.fromArray(positions,i),bb.expandByPoint(vector)}void 0!==positions&&0!==positions.length||(this.boundingBox.min.set(0,0,0),this.boundingBox.max.set(0,0,0)),(isNaN(this.boundingBox.min.x)||isNaN(this.boundingBox.min.y)||isNaN(this.boundingBox.min.z))&&console.error('THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.',this)}}(),computeBoundingSphere:function(){var box=new THREE.Box3,vector=new THREE.Vector3;return function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere);var positions=this.attributes.position.array;if(positions){box.makeEmpty();for(var center=this.boundingSphere.center,i=0,il=positions.length;il>i;i+=3)vector.fromArray(positions,i),box.expandByPoint(vector);box.center(center);for(var maxRadiusSq=0,i=0,il=positions.length;il>i;i+=3)vector.fromArray(positions,i),maxRadiusSq=Math.max(maxRadiusSq,center.distanceToSquared(vector));this.boundingSphere.radius=Math.sqrt(maxRadiusSq),isNaN(this.boundingSphere.radius)&&console.error('THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.',this)}}}(),computeFaceNormals:function(){},computeVertexNormals:function(){var index=this.index,attributes=this.attributes,groups=this.groups;if(attributes.position){var positions=attributes.position.array;if(void 0===attributes.normal)this.addAttribute("normal",new THREE.BufferAttribute(new Float32Array(positions.length),3));else for(var normals=attributes.normal.array,i=0,il=normals.length;il>i;i++)normals[i]=0;var vA,vB,vC,normals=attributes.normal.array,pA=new THREE.Vector3,pB=new THREE.Vector3,pC=new THREE.Vector3,cb=new THREE.Vector3,ab=new THREE.Vector3;if(index){var indices=index.array;0===groups.length&&this.addGroup(0,indices.length);for(var j=0,jl=groups.length;jl>j;++j)for(var group=groups[j],start=group.start,count=group.count,i=start,il=start+count;il>i;i+=3)vA=3*indices[i+0],vB=3*indices[i+1],vC=3*indices[i+2],pA.fromArray(positions,vA),pB.fromArray(positions,vB),pC.fromArray(positions,vC),cb.subVectors(pC,pB),ab.subVectors(pA,pB),cb.cross(ab),normals[vA]+=cb.x,normals[vA+1]+=cb.y,normals[vA+2]+=cb.z,normals[vB]+=cb.x,normals[vB+1]+=cb.y,normals[vB+2]+=cb.z,normals[vC]+=cb.x,normals[vC+1]+=cb.y,normals[vC+2]+=cb.z}else for(var i=0,il=positions.length;il>i;i+=9)pA.fromArray(positions,i),pB.fromArray(positions,i+3),pC.fromArray(positions,i+6),cb.subVectors(pC,pB),ab.subVectors(pA,pB),cb.cross(ab),normals[i]=cb.x,normals[i+1]=cb.y,normals[i+2]=cb.z,normals[i+3]=cb.x,normals[i+4]=cb.y,normals[i+5]=cb.z,normals[i+6]=cb.x,normals[i+7]=cb.y,normals[i+8]=cb.z;this.normalizeNormals(),attributes.normal.needsUpdate=!0}},computeTangents:function(){console.warn("THREE.BufferGeometry: .computeTangents() has been removed.")},computeOffsets:function(size){console.warn("THREE.BufferGeometry: .computeOffsets() has been removed.")},merge:function(geometry,offset){if(geometry instanceof THREE.BufferGeometry==!1)return void console.error("THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.",geometry);void 0===offset&&(offset=0);var attributes=this.attributes;for(var key in attributes)if(void 0!==geometry.attributes[key])for(var attribute1=attributes[key],attributeArray1=attribute1.array,attribute2=geometry.attributes[key],attributeArray2=attribute2.array,attributeSize=attribute2.itemSize,i=0,j=attributeSize*offset;ii;i+=3)x=normals[i],y=normals[i+1],z=normals[i+2],n=1/Math.sqrt(x*x+y*y+z*z),normals[i]*=n,normals[i+1]*=n,normals[i+2]*=n},toJSON:function(){var data={metadata:{version:4.4,type:"BufferGeometry",generator:"BufferGeometry.toJSON"}};if(data.uuid=this.uuid,data.type=this.type,""!==this.name&&(data.name=this.name),void 0!==this.parameters){var parameters=this.parameters;for(var key in parameters)void 0!==parameters[key]&&(data[key]=parameters[key]);return data}data.data={attributes:{}};var index=this.index;if(null!==index){var array=Array.prototype.slice.call(index.array);data.data.index={type:index.array.constructor.name,array:array}}var attributes=this.attributes;for(var key in attributes){var attribute=attributes[key],array=Array.prototype.slice.call(attribute.array);data.data.attributes[key]={itemSize:attribute.itemSize,type:attribute.array.constructor.name,array:array}}var groups=this.groups;groups.length>0&&(data.data.groups=JSON.parse(JSON.stringify(groups)));var boundingSphere=this.boundingSphere;return null!==boundingSphere&&(data.data.boundingSphere={center:boundingSphere.center.toArray(),radius:boundingSphere.radius}),data},clone:function(){return(new this.constructor).copy(this);
+},copy:function(source){var index=source.index;null!==index&&this.setIndex(index.clone());var attributes=source.attributes;for(var name in attributes){var attribute=attributes[name];this.addAttribute(name,attribute.clone())}for(var groups=source.groups,i=0,l=groups.length;l>i;i++){var group=groups[i];this.addGroup(group.start,group.count)}return this},dispose:function(){this.dispatchEvent({type:"dispose"})}},THREE.EventDispatcher.prototype.apply(THREE.BufferGeometry.prototype),THREE.BufferGeometry.MaxIndex=65535,THREE.InstancedBufferGeometry=function(){THREE.BufferGeometry.call(this),this.type="InstancedBufferGeometry",this.maxInstancedCount=void 0},THREE.InstancedBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.InstancedBufferGeometry.prototype.constructor=THREE.InstancedBufferGeometry,THREE.InstancedBufferGeometry.prototype.addGroup=function(start,count,instances){this.groups.push({start:start,count:count,instances:instances})},THREE.InstancedBufferGeometry.prototype.copy=function(source){var index=source.index;null!==index&&this.setIndex(index.clone());var attributes=source.attributes;for(var name in attributes){var attribute=attributes[name];this.addAttribute(name,attribute.clone())}for(var groups=source.groups,i=0,l=groups.length;l>i;i++){var group=groups[i];this.addGroup(group.start,group.count,group.instances)}return this},THREE.EventDispatcher.prototype.apply(THREE.InstancedBufferGeometry.prototype),THREE.AnimationAction=function(clip,startTime,timeScale,weight,loop){if(void 0===clip)throw new Error("clip is null");this.clip=clip,this.localRoot=null,this.startTime=startTime||0,this.timeScale=timeScale||1,this.weight=weight||1,this.loop=loop||THREE.LoopRepeat,this.loopCount=0,this.enabled=!0,this.actionTime=-this.startTime,this.clipTime=0,this.propertyBindings=[]},THREE.AnimationAction.prototype={constructor:THREE.AnimationAction,setLocalRoot:function(localRoot){return this.localRoot=localRoot,this},updateTime:function(clipDeltaTime){var previousClipTime=this.clipTime,previousLoopCount=this.loopCount,duration=(this.actionTime,this.clip.duration);if(this.actionTime=this.actionTime+clipDeltaTime,this.loop===THREE.LoopOnce)return this.loopCount=0,this.clipTime=Math.min(Math.max(this.actionTime,0),duration),this.clipTime!==previousClipTime&&(this.clipTime===duration?this.mixer.dispatchEvent({type:"finished",action:this,direction:1}):0===this.clipTime&&this.mixer.dispatchEvent({type:"finished",action:this,direction:-1})),this.clipTime;this.loopCount=Math.floor(this.actionTime/duration);var newClipTime=this.actionTime-this.loopCount*duration;return newClipTime%=duration,this.loop==THREE.LoopPingPong&&1===Math.abs(this.loopCount%2)&&(newClipTime=duration-newClipTime),this.clipTime=newClipTime,this.loopCount!==previousLoopCount&&this.mixer.dispatchEvent({type:"loop",action:this,loopDelta:this.loopCount-this.loopCount}),this.clipTime},syncWith:function(action){return this.actionTime=action.actionTime,this.timeScale=action.timeScale,this},warpToDuration:function(duration){return this.timeScale=this.clip.duration/duration,this},init:function(time){return this.clipTime=time-this.startTime,this},update:function(clipDeltaTime){this.updateTime(clipDeltaTime);var clipResults=this.clip.getAt(this.clipTime);return clipResults},getTimeScaleAt:function(time){return this.timeScale.getAt?this.timeScale.getAt(time):this.timeScale},getWeightAt:function(time){return this.weight.getAt?this.weight.getAt(time):this.weight}},THREE.AnimationClip=function(name,duration,tracks){if(this.name=name,this.tracks=tracks,this.duration=void 0!==duration?duration:-1,this.duration<0)for(var i=0;ii;i++){var keys=[];keys.push({time:(i+numMorphTargets-1)%numMorphTargets,value:0}),keys.push({time:i,value:1}),keys.push({time:(i+1)%numMorphTargets,value:0}),keys.sort(THREE.KeyframeTrack.keyComparer),0===keys[0].time&&keys.push({time:numMorphTargets,value:keys[0].value}),tracks.push(new THREE.NumberKeyframeTrack(".morphTargetInfluences["+morphTargetSequence[i].name+"]",keys).scale(1/fps))}return new THREE.AnimationClip(name,-1,tracks)},THREE.AnimationClip.findByName=function(clipArray,name){for(var i=0;ii;i++){var morphTarget=morphTargets[i],parts=morphTarget.name.match(pattern);if(parts&&parts.length>1){var name=parts[1],animationMorphTargets=animationToMorphTargets[name];animationMorphTargets||(animationToMorphTargets[name]=animationMorphTargets=[]),animationMorphTargets.push(morphTarget)}}var clips=[];for(var name in animationToMorphTargets)clips.push(THREE.AnimationClip.CreateFromMorphTargetSequence(name,animationToMorphTargets[name],fps));return clips},THREE.AnimationClip.parse=function(json){for(var tracks=[],i=0;i0?new trackType(trackName,keys):null},tracks=[],clipName=animation.name||"default",duration=animation.length||-1,fps=animation.fps||30,hierarchyTracks=animation.hierarchy||[],h=0;halpha?a:b},lerp_boolean_immediate:function(a,b,alpha){return a},lerp_string:function(a,b,alpha){return.5>alpha?a:b},lerp_string_immediate:function(a,b,alpha){return a},getLerpFunc:function(exemplarValue,interTrack){if(void 0===exemplarValue||null===exemplarValue)throw new Error("examplarValue is null");var typeName=typeof exemplarValue;switch(typeName){case"object":if(exemplarValue.lerp)return THREE.AnimationUtils.lerp_object;if(exemplarValue.slerp)return THREE.AnimationUtils.slerp_object;break;case"number":return THREE.AnimationUtils.lerp_number;case"boolean":return interTrack?THREE.AnimationUtils.lerp_boolean:THREE.AnimationUtils.lerp_boolean_immediate;case"string":return interTrack?THREE.AnimationUtils.lerp_string:THREE.AnimationUtils.lerp_string_immediate}}},THREE.KeyframeTrack=function(name,keys){if(void 0===name)throw new Error("track name is undefined");if(void 0===keys||0===keys.length)throw new Error("no keys in track named "+name);this.name=name,this.keys=keys,this.lastIndex=0,this.validate(),this.optimize()},THREE.KeyframeTrack.prototype={constructor:THREE.KeyframeTrack,getAt:function(time){for(;this.lastIndex=this.keys[this.lastIndex].time;)this.lastIndex++;for(;this.lastIndex>0&&time=this.keys.length)return this.setResult(this.keys[this.keys.length-1].value),this.result;if(0===this.lastIndex)return this.setResult(this.keys[0].value),this.result;var prevKey=this.keys[this.lastIndex-1];if(this.setResult(prevKey.value),prevKey.constantToNext)return this.result;var currentKey=this.keys[this.lastIndex],alpha=(time-prevKey.time)/(currentKey.time-prevKey.time);return this.result=this.lerpValues(this.result,currentKey.value,alpha),this.result},shift:function(timeOffset){if(0!==timeOffset)for(var i=0;i0&&this.keys[i]>=endTime;i++)lastKeysToRemove++;return firstKeysToRemove+lastKeysToRemove>0&&(this.keys=this.keys.splice(firstKeysToRemove,this.keys.length-lastKeysToRemove-firstKeysToRemove)),this},validate:function(){var prevKey=null;if(0===this.keys.length)return void console.error(" track is empty, no keys",this);for(var i=0;icurrKey.time)return void console.error(" key.time is less than previous key time, out of order keys",this,i,currKey,prevKey);prevKey=currKey}return this},optimize:function(){var newKeys=[],prevKey=this.keys[0];newKeys.push(prevKey);for(var i=(THREE.AnimationUtils.getEqualsFunc(prevKey.value),1);i0&&(null===this.cumulativeValue&&(this.cumulativeValue=THREE.AnimationUtils.clone(value)),this.cumulativeWeight=weight);else{var lerpAlpha=weight/(this.cumulativeWeight+weight);this.cumulativeValue=this.lerpValue(this.cumulativeValue,value,lerpAlpha),this.cumulativeWeight+=weight}},unbind:function(){this.isBound&&(this.setValue(this.originalValue),this.setValue=null,this.getValue=null,this.lerpValue=null,this.equalsValue=null,this.triggerDirty=null,this.isBound=!1)},bind:function(){if(!this.isBound){var targetObject=this.node;if(!targetObject)return void console.error(" trying to update node for track: "+this.trackName+" but it wasn't found.");if(this.objectName){if("materials"===this.objectName){if(!targetObject.material)return void console.error(" can not bind to material as node does not have a material",this);if(!targetObject.material.materials)return void console.error(" can not bind to material.materials as node.material does not have a materials array",this);targetObject=targetObject.material.materials}else if("bones"===this.objectName){if(!targetObject.skeleton)return void console.error(" can not bind to bones as node does not have a skeleton",this);targetObject=targetObject.skeleton.bones;for(var i=0;i0){if(this.cumulativeWeight<1){var remainingWeight=1-this.cumulativeWeight,lerpAlpha=remainingWeight/(this.cumulativeWeight+remainingWeight);this.cumulativeValue=this.lerpValue(this.cumulativeValue,this.originalValue,lerpAlpha)}var valueChanged=this.setValue(this.cumulativeValue);valueChanged&&this.triggerDirty&&this.triggerDirty(),this.cumulativeValue=null,this.cumulativeWeight=0}}},THREE.PropertyBinding.parseTrackName=function(trackName){var re=/^(([\w]+\/)*)([\w-\d]+)?(\.([\w]+)(\[([\w\d\[\]\_. ]+)\])?)?(\.([\w.]+)(\[([\w\d\[\]\_. ]+)\])?)$/,matches=re.exec(trackName);if(!matches)throw new Error("cannot parse trackName at all: "+trackName);matches.index===re.lastIndex&&re.lastIndex++;var results={directoryName:matches[1],nodeName:matches[3],objectName:matches[5],objectIndex:matches[7],propertyName:matches[9],propertyIndex:matches[11]};if(null===results.propertyName||0===results.propertyName.length)throw new Error("can not parse propertyName from trackName: "+trackName);return results},THREE.PropertyBinding.findNode=function(root,nodeName){function searchSkeleton(skeleton){for(var i=0;ialpha?value0:value1},THREE.StringKeyframeTrack.prototype.compareValues=function(value0,value1){return value0===value1},THREE.StringKeyframeTrack.prototype.clone=function(){for(var clonedKeys=[],i=0;ialpha?value0:value1},THREE.BooleanKeyframeTrack.prototype.compareValues=function(value0,value1){return value0===value1},THREE.BooleanKeyframeTrack.prototype.clone=function(){for(var clonedKeys=[],i=0;ii;i+=2){var regex=handlers[i],loader=handlers[i+1];if(regex.test(file))return loader}return null}},THREE.XHRLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager},THREE.XHRLoader.prototype={constructor:THREE.XHRLoader,load:function(url,onLoad,onProgress,onError){var scope=this,cached=THREE.Cache.get(url);if(void 0!==cached)return onLoad&&setTimeout(function(){onLoad(cached)},0),cached;var request=new XMLHttpRequest;return request.open("GET",url,!0),request.addEventListener("load",function(event){var response=event.target.response;THREE.Cache.add(url,response),onLoad&&onLoad(response),scope.manager.itemEnd(url)},!1),void 0!==onProgress&&request.addEventListener("progress",function(event){onProgress(event)},!1),request.addEventListener("error",function(event){onError&&onError(event),scope.manager.itemError(url)},!1),void 0!==this.crossOrigin&&(request.crossOrigin=this.crossOrigin),void 0!==this.responseType&&(request.responseType=this.responseType),void 0!==this.withCredentials&&(request.withCredentials=this.withCredentials),request.send(null),scope.manager.itemStart(url),request},setResponseType:function(value){this.responseType=value},setCrossOrigin:function(value){this.crossOrigin=value},setWithCredentials:function(value){this.withCredentials=value}},THREE.ImageLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager},THREE.ImageLoader.prototype={constructor:THREE.ImageLoader,load:function(url,onLoad,onProgress,onError){var scope=this,cached=THREE.Cache.get(url);if(void 0!==cached)return scope.manager.itemStart(url),onLoad?setTimeout(function(){onLoad(cached),scope.manager.itemEnd(url)},0):scope.manager.itemEnd(url),cached;var image=document.createElement("img");return image.addEventListener("load",function(event){THREE.Cache.add(url,this),onLoad&&onLoad(this),scope.manager.itemEnd(url)},!1),void 0!==onProgress&&image.addEventListener("progress",function(event){onProgress(event)},!1),image.addEventListener("error",function(event){onError&&onError(event),scope.manager.itemError(url)},!1),void 0!==this.crossOrigin&&(image.crossOrigin=this.crossOrigin),scope.manager.itemStart(url),image.src=url,image},setCrossOrigin:function(value){this.crossOrigin=value}},THREE.JSONLoader=function(manager){"boolean"==typeof manager&&(console.warn("THREE.JSONLoader: showStatus parameter has been removed from constructor."),manager=void 0),this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager,this.withCredentials=!1},THREE.JSONLoader.prototype={constructor:THREE.JSONLoader,get statusDomElement(){return void 0===this._statusDomElement&&(this._statusDomElement=document.createElement("div")),console.warn("THREE.JSONLoader: .statusDomElement has been removed."),this._statusDomElement},load:function(url,onLoad,onProgress,onError){var scope=this,texturePath=this.texturePath&&"string"==typeof this.texturePath?this.texturePath:THREE.Loader.prototype.extractUrlBase(url),loader=new THREE.XHRLoader(this.manager);loader.setCrossOrigin(this.crossOrigin),loader.setWithCredentials(this.withCredentials),loader.load(url,function(text){var json=JSON.parse(text),metadata=json.metadata;if(void 0!==metadata){if("object"===metadata.type)return void console.error("THREE.JSONLoader: "+url+" should be loaded with THREE.ObjectLoader instead.");if("scene"===metadata.type)return void console.error("THREE.JSONLoader: "+url+" should be loaded with THREE.SceneLoader instead.")}var object=scope.parse(json,texturePath);onLoad(object.geometry,object.materials)})},setCrossOrigin:function(value){this.crossOrigin=value},setTexturePath:function(value){this.texturePath=value},parse:function(json,texturePath){function parseModel(scale){function isBitSet(value,position){return value&1<i;i++)geometry.faceVertexUvs[i]=[]}for(offset=0,zLength=vertices.length;zLength>offset;)vertex=new THREE.Vector3,vertex.x=vertices[offset++]*scale,vertex.y=vertices[offset++]*scale,vertex.z=vertices[offset++]*scale,geometry.vertices.push(vertex);for(offset=0,zLength=faces.length;zLength>offset;)if(type=faces[offset++],isQuad=isBitSet(type,0),hasMaterial=isBitSet(type,1),hasFaceVertexUv=isBitSet(type,3),hasFaceNormal=isBitSet(type,4),hasFaceVertexNormal=isBitSet(type,5),hasFaceColor=isBitSet(type,6),hasFaceVertexColor=isBitSet(type,7),isQuad){if(faceA=new THREE.Face3,faceA.a=faces[offset],faceA.b=faces[offset+1],faceA.c=faces[offset+3],faceB=new THREE.Face3,faceB.a=faces[offset+1],faceB.b=faces[offset+2],faceB.c=faces[offset+3],offset+=4,hasMaterial&&(materialIndex=faces[offset++],faceA.materialIndex=materialIndex,faceB.materialIndex=materialIndex),fi=geometry.faces.length,hasFaceVertexUv)for(i=0;nUvLayers>i;i++)for(uvLayer=json.uvs[i],geometry.faceVertexUvs[i][fi]=[],geometry.faceVertexUvs[i][fi+1]=[],j=0;4>j;j++)uvIndex=faces[offset++],u=uvLayer[2*uvIndex],v=uvLayer[2*uvIndex+1],uv=new THREE.Vector2(u,v),2!==j&&geometry.faceVertexUvs[i][fi].push(uv),0!==j&&geometry.faceVertexUvs[i][fi+1].push(uv);if(hasFaceNormal&&(normalIndex=3*faces[offset++],faceA.normal.set(normals[normalIndex++],normals[normalIndex++],normals[normalIndex]),faceB.normal.copy(faceA.normal)),hasFaceVertexNormal)for(i=0;4>i;i++)normalIndex=3*faces[offset++],normal=new THREE.Vector3(normals[normalIndex++],normals[normalIndex++],normals[normalIndex]),2!==i&&faceA.vertexNormals.push(normal),0!==i&&faceB.vertexNormals.push(normal);if(hasFaceColor&&(colorIndex=faces[offset++],hex=colors[colorIndex],faceA.color.setHex(hex),faceB.color.setHex(hex)),hasFaceVertexColor)for(i=0;4>i;i++)colorIndex=faces[offset++],hex=colors[colorIndex],2!==i&&faceA.vertexColors.push(new THREE.Color(hex)),0!==i&&faceB.vertexColors.push(new THREE.Color(hex));geometry.faces.push(faceA),geometry.faces.push(faceB)}else{if(face=new THREE.Face3,face.a=faces[offset++],face.b=faces[offset++],face.c=faces[offset++],hasMaterial&&(materialIndex=faces[offset++],face.materialIndex=materialIndex),fi=geometry.faces.length,hasFaceVertexUv)for(i=0;nUvLayers>i;i++)for(uvLayer=json.uvs[i],geometry.faceVertexUvs[i][fi]=[],j=0;3>j;j++)uvIndex=faces[offset++],u=uvLayer[2*uvIndex],v=uvLayer[2*uvIndex+1],uv=new THREE.Vector2(u,v),geometry.faceVertexUvs[i][fi].push(uv);if(hasFaceNormal&&(normalIndex=3*faces[offset++],face.normal.set(normals[normalIndex++],normals[normalIndex++],normals[normalIndex])),hasFaceVertexNormal)for(i=0;3>i;i++)normalIndex=3*faces[offset++],normal=new THREE.Vector3(normals[normalIndex++],normals[normalIndex++],normals[normalIndex]),face.vertexNormals.push(normal);if(hasFaceColor&&(colorIndex=faces[offset++],face.color.setHex(colors[colorIndex])),hasFaceVertexColor)for(i=0;3>i;i++)colorIndex=faces[offset++],face.vertexColors.push(new THREE.Color(colors[colorIndex]));geometry.faces.push(face)}}function parseSkin(){var influencesPerVertex=void 0!==json.influencesPerVertex?json.influencesPerVertex:2;if(json.skinWeights)for(var i=0,l=json.skinWeights.length;l>i;i+=influencesPerVertex){var x=json.skinWeights[i],y=influencesPerVertex>1?json.skinWeights[i+1]:0,z=influencesPerVertex>2?json.skinWeights[i+2]:0,w=influencesPerVertex>3?json.skinWeights[i+3]:0;geometry.skinWeights.push(new THREE.Vector4(x,y,z,w))}if(json.skinIndices)for(var i=0,l=json.skinIndices.length;l>i;i+=influencesPerVertex){var a=json.skinIndices[i],b=influencesPerVertex>1?json.skinIndices[i+1]:0,c=influencesPerVertex>2?json.skinIndices[i+2]:0,d=influencesPerVertex>3?json.skinIndices[i+3]:0;geometry.skinIndices.push(new THREE.Vector4(a,b,c,d))}geometry.bones=json.bones,geometry.bones&&geometry.bones.length>0&&(geometry.skinWeights.length!==geometry.skinIndices.length||geometry.skinIndices.length!==geometry.vertices.length)&&console.warn("When skinning, number of vertices ("+geometry.vertices.length+"), skinIndices ("+geometry.skinIndices.length+"), and skinWeights ("+geometry.skinWeights.length+") should match.")}function parseMorphing(scale){if(void 0!==json.morphTargets)for(var i=0,l=json.morphTargets.length;l>i;i++){geometry.morphTargets[i]={},geometry.morphTargets[i].name=json.morphTargets[i].name,geometry.morphTargets[i].vertices=[];for(var dstVertices=geometry.morphTargets[i].vertices,srcVertices=json.morphTargets[i].vertices,v=0,vl=srcVertices.length;vl>v;v+=3){var vertex=new THREE.Vector3;vertex.x=srcVertices[v]*scale,vertex.y=srcVertices[v+1]*scale,vertex.z=srcVertices[v+2]*scale,dstVertices.push(vertex)}}if(void 0!==json.morphColors&&json.morphColors.length>0){console.warn('THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.');for(var faces=geometry.faces,morphColors=json.morphColors[0].colors,i=0,l=faces.length;l>i;i++)faces[i].color.fromArray(morphColors,3*i)}}function parseAnimations(){var outputAnimations=[],animations=[];void 0!==json.animation&&animations.push(json.animation),void 0!==json.animations&&(json.animations.length?animations=animations.concat(json.animations):animations.push(json.animations));for(var i=0;i0&&(geometry.animations=outputAnimations)}var geometry=new THREE.Geometry,scale=void 0!==json.scale?1/json.scale:1;if(parseModel(scale),parseSkin(),parseMorphing(scale),parseAnimations(),geometry.computeFaceNormals(),geometry.computeBoundingSphere(),void 0===json.materials||0===json.materials.length)return{geometry:geometry};var materials=THREE.Loader.prototype.initMaterials(json.materials,texturePath,this.crossOrigin);return{geometry:geometry,materials:materials}}},THREE.LoadingManager=function(onLoad,onProgress,onError){var scope=this,isLoading=!1,itemsLoaded=0,itemsTotal=0;this.onStart=void 0,this.onLoad=onLoad,this.onProgress=onProgress,this.onError=onError,this.itemStart=function(url){itemsTotal++,isLoading===!1&&void 0!==scope.onStart&&scope.onStart(url,itemsLoaded,itemsTotal),isLoading=!0},this.itemEnd=function(url){itemsLoaded++,void 0!==scope.onProgress&&scope.onProgress(url,itemsLoaded,itemsTotal),itemsLoaded===itemsTotal&&(isLoading=!1,void 0!==scope.onLoad&&scope.onLoad())},this.itemError=function(url){void 0!==scope.onError&&scope.onError(url)}},THREE.DefaultLoadingManager=new THREE.LoadingManager,THREE.BufferGeometryLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager},THREE.BufferGeometryLoader.prototype={constructor:THREE.BufferGeometryLoader,load:function(url,onLoad,onProgress,onError){var scope=this,loader=new THREE.XHRLoader(scope.manager);loader.setCrossOrigin(this.crossOrigin),loader.load(url,function(text){onLoad(scope.parse(JSON.parse(text)))},onProgress,onError)},setCrossOrigin:function(value){this.crossOrigin=value},parse:function(json){var geometry=new THREE.BufferGeometry,index=json.data.index;if(void 0!==index){var typedArray=new self[index.type](index.array);geometry.setIndex(new THREE.BufferAttribute(typedArray,1))}var attributes=json.data.attributes;for(var key in attributes){var attribute=attributes[key],typedArray=new self[attribute.type](attribute.array);geometry.addAttribute(key,new THREE.BufferAttribute(typedArray,attribute.itemSize))}var groups=json.data.groups||json.data.drawcalls||json.data.offsets;if(void 0!==groups)for(var i=0,n=groups.length;i!==n;++i){var group=groups[i];geometry.addGroup(group.start,group.count)}var boundingSphere=json.data.boundingSphere;if(void 0!==boundingSphere){var center=new THREE.Vector3;void 0!==boundingSphere.center&¢er.fromArray(boundingSphere.center),geometry.boundingSphere=new THREE.Sphere(center,boundingSphere.radius)}return geometry}},THREE.MaterialLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager,this.textures={}},THREE.MaterialLoader.prototype={constructor:THREE.MaterialLoader,load:function(url,onLoad,onProgress,onError){var scope=this,loader=new THREE.XHRLoader(scope.manager);loader.setCrossOrigin(this.crossOrigin),loader.load(url,function(text){onLoad(scope.parse(JSON.parse(text)))},onProgress,onError)},setCrossOrigin:function(value){this.crossOrigin=value},setTextures:function(value){this.textures=value},getTexture:function(name){var textures=this.textures;return void 0===textures[name]&&console.warn("THREE.MaterialLoader: Undefined texture",name),textures[name]},parse:function(json){var material=new THREE[json.type];if(material.uuid=json.uuid,void 0!==json.name&&(material.name=json.name),void 0!==json.color&&material.color.setHex(json.color),void 0!==json.emissive&&material.emissive.setHex(json.emissive),void 0!==json.specular&&material.specular.setHex(json.specular),void 0!==json.shininess&&(material.shininess=json.shininess),void 0!==json.uniforms&&(material.uniforms=json.uniforms),void 0!==json.vertexShader&&(material.vertexShader=json.vertexShader),void 0!==json.fragmentShader&&(material.fragmentShader=json.fragmentShader),void 0!==json.vertexColors&&(material.vertexColors=json.vertexColors),void 0!==json.shading&&(material.shading=json.shading),void 0!==json.blending&&(material.blending=json.blending),void 0!==json.side&&(material.side=json.side),void 0!==json.opacity&&(material.opacity=json.opacity),void 0!==json.transparent&&(material.transparent=json.transparent),void 0!==json.alphaTest&&(material.alphaTest=json.alphaTest),void 0!==json.depthTest&&(material.depthTest=json.depthTest),void 0!==json.depthWrite&&(material.depthWrite=json.depthWrite),void 0!==json.wireframe&&(material.wireframe=json.wireframe),void 0!==json.wireframeLinewidth&&(material.wireframeLinewidth=json.wireframeLinewidth),void 0!==json.size&&(material.size=json.size),void 0!==json.sizeAttenuation&&(material.sizeAttenuation=json.sizeAttenuation),void 0!==json.map&&(material.map=this.getTexture(json.map)),void 0!==json.alphaMap&&(material.alphaMap=this.getTexture(json.alphaMap),material.transparent=!0),void 0!==json.bumpMap&&(material.bumpMap=this.getTexture(json.bumpMap)),void 0!==json.bumpScale&&(material.bumpScale=json.bumpScale),void 0!==json.normalMap&&(material.normalMap=this.getTexture(json.normalMap)),json.normalScale&&(material.normalScale=new THREE.Vector2(json.normalScale,json.normalScale)),void 0!==json.displacementMap&&(material.displacementMap=this.getTexture(json.displacementMap)),void 0!==json.displacementScale&&(material.displacementScale=json.displacementScale),void 0!==json.displacementBias&&(material.displacementBias=json.displacementBias),void 0!==json.specularMap&&(material.specularMap=this.getTexture(json.specularMap)),void 0!==json.envMap&&(material.envMap=this.getTexture(json.envMap),material.combine=THREE.MultiplyOperation),json.reflectivity&&(material.reflectivity=json.reflectivity),void 0!==json.lightMap&&(material.lightMap=this.getTexture(json.lightMap)),void 0!==json.lightMapIntensity&&(material.lightMapIntensity=json.lightMapIntensity),void 0!==json.aoMap&&(material.aoMap=this.getTexture(json.aoMap)),void 0!==json.aoMapIntensity&&(material.aoMapIntensity=json.aoMapIntensity),void 0!==json.materials)for(var i=0,l=json.materials.length;l>i;i++)material.materials.push(this.parse(json.materials[i]));return material}},THREE.ObjectLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager,this.texturePath=""},THREE.ObjectLoader.prototype={constructor:THREE.ObjectLoader,load:function(url,onLoad,onProgress,onError){""===this.texturePath&&(this.texturePath=url.substring(0,url.lastIndexOf("/")+1));var scope=this,loader=new THREE.XHRLoader(scope.manager);loader.setCrossOrigin(this.crossOrigin),loader.load(url,function(text){scope.parse(JSON.parse(text),onLoad)},onProgress,onError)},setTexturePath:function(value){this.texturePath=value},setCrossOrigin:function(value){this.crossOrigin=value},parse:function(json,onLoad){var geometries=this.parseGeometries(json.geometries),images=this.parseImages(json.images,function(){void 0!==onLoad&&onLoad(object)}),textures=this.parseTextures(json.textures,images),materials=this.parseMaterials(json.materials,textures),object=this.parseObject(json.object,geometries,materials);return json.animations&&(object.animations=this.parseAnimations(json.animations)),void 0!==json.images&&0!==json.images.length||void 0!==onLoad&&onLoad(object),object},parseGeometries:function(json){var geometries={};if(void 0!==json)for(var geometryLoader=new THREE.JSONLoader,bufferGeometryLoader=new THREE.BufferGeometryLoader,i=0,l=json.length;l>i;i++){var geometry,data=json[i];switch(data.type){case"PlaneGeometry":case"PlaneBufferGeometry":geometry=new THREE[data.type](data.width,data.height,data.widthSegments,data.heightSegments);break;case"BoxGeometry":case"CubeGeometry":geometry=new THREE.BoxGeometry(data.width,data.height,data.depth,data.widthSegments,data.heightSegments,data.depthSegments);break;case"CircleBufferGeometry":geometry=new THREE.CircleBufferGeometry(data.radius,data.segments,data.thetaStart,data.thetaLength);break;case"CircleGeometry":geometry=new THREE.CircleGeometry(data.radius,data.segments,data.thetaStart,data.thetaLength);break;case"CylinderGeometry":geometry=new THREE.CylinderGeometry(data.radiusTop,data.radiusBottom,data.height,data.radialSegments,data.heightSegments,data.openEnded,data.thetaStart,data.thetaLength);break;case"SphereGeometry":geometry=new THREE.SphereGeometry(data.radius,data.widthSegments,data.heightSegments,data.phiStart,data.phiLength,data.thetaStart,data.thetaLength);break;case"SphereBufferGeometry":geometry=new THREE.SphereBufferGeometry(data.radius,data.widthSegments,data.heightSegments,data.phiStart,data.phiLength,data.thetaStart,data.thetaLength);break;case"DodecahedronGeometry":geometry=new THREE.DodecahedronGeometry(data.radius,data.detail);break;case"IcosahedronGeometry":geometry=new THREE.IcosahedronGeometry(data.radius,data.detail);break;case"OctahedronGeometry":geometry=new THREE.OctahedronGeometry(data.radius,data.detail);break;case"TetrahedronGeometry":geometry=new THREE.TetrahedronGeometry(data.radius,data.detail);break;case"RingGeometry":geometry=new THREE.RingGeometry(data.innerRadius,data.outerRadius,data.thetaSegments,data.phiSegments,data.thetaStart,data.thetaLength);break;case"TorusGeometry":geometry=new THREE.TorusGeometry(data.radius,data.tube,data.radialSegments,data.tubularSegments,data.arc);break;case"TorusKnotGeometry":geometry=new THREE.TorusKnotGeometry(data.radius,data.tube,data.radialSegments,data.tubularSegments,data.p,data.q,data.heightScale);break;case"BufferGeometry":geometry=bufferGeometryLoader.parse(data);break;case"Geometry":
+geometry=geometryLoader.parse(data.data,this.texturePath).geometry;break;default:console.warn('THREE.ObjectLoader: Unsupported geometry type "'+data.type+'"');continue}geometry.uuid=data.uuid,void 0!==data.name&&(geometry.name=data.name),geometries[data.uuid]=geometry}return geometries},parseMaterials:function(json,textures){var materials={};if(void 0!==json){var loader=new THREE.MaterialLoader;loader.setTextures(textures);for(var i=0,l=json.length;l>i;i++){var material=loader.parse(json[i]);materials[material.uuid]=material}}return materials},parseAnimations:function(json){for(var animations=[],i=0;i0){var manager=new THREE.LoadingManager(onLoad),loader=new THREE.ImageLoader(manager);loader.setCrossOrigin(this.crossOrigin);for(var i=0,l=json.length;l>i;i++){var image=json[i],path=/^(\/\/)|([a-z]+:(\/\/)?)/i.test(image.url)?image.url:scope.texturePath+image.url;images[image.uuid]=loadImage(path)}}return images},parseTextures:function(json,images){function parseConstant(value){return"number"==typeof value?value:(console.warn("THREE.ObjectLoader.parseTexture: Constant should be in numeric form.",value),THREE[value])}var textures={};if(void 0!==json)for(var i=0,l=json.length;l>i;i++){var data=json[i];void 0===data.image&&console.warn('THREE.ObjectLoader: No "image" specified for',data.uuid),void 0===images[data.image]&&console.warn("THREE.ObjectLoader: Undefined image",data.image);var texture=new THREE.Texture(images[data.image]);texture.needsUpdate=!0,texture.uuid=data.uuid,void 0!==data.name&&(texture.name=data.name),void 0!==data.mapping&&(texture.mapping=parseConstant(data.mapping)),void 0!==data.offset&&(texture.offset=new THREE.Vector2(data.offset[0],data.offset[1])),void 0!==data.repeat&&(texture.repeat=new THREE.Vector2(data.repeat[0],data.repeat[1])),void 0!==data.minFilter&&(texture.minFilter=parseConstant(data.minFilter)),void 0!==data.magFilter&&(texture.magFilter=parseConstant(data.magFilter)),void 0!==data.anisotropy&&(texture.anisotropy=data.anisotropy),Array.isArray(data.wrap)&&(texture.wrapS=parseConstant(data.wrap[0]),texture.wrapT=parseConstant(data.wrap[1])),textures[data.uuid]=texture}return textures},parseObject:function(){var matrix=new THREE.Matrix4;return function(data,geometries,materials){function getGeometry(name){return void 0===geometries[name]&&console.warn("THREE.ObjectLoader: Undefined geometry",name),geometries[name]}function getMaterial(name){return void 0!==name?(void 0===materials[name]&&console.warn("THREE.ObjectLoader: Undefined material",name),materials[name]):void 0}var object;switch(data.type){case"Scene":object=new THREE.Scene;break;case"PerspectiveCamera":object=new THREE.PerspectiveCamera(data.fov,data.aspect,data.near,data.far);break;case"OrthographicCamera":object=new THREE.OrthographicCamera(data.left,data.right,data.top,data.bottom,data.near,data.far);break;case"AmbientLight":object=new THREE.AmbientLight(data.color);break;case"DirectionalLight":object=new THREE.DirectionalLight(data.color,data.intensity);break;case"PointLight":object=new THREE.PointLight(data.color,data.intensity,data.distance,data.decay);break;case"SpotLight":object=new THREE.SpotLight(data.color,data.intensity,data.distance,data.angle,data.exponent,data.decay);break;case"HemisphereLight":object=new THREE.HemisphereLight(data.color,data.groundColor,data.intensity);break;case"Mesh":object=new THREE.Mesh(getGeometry(data.geometry),getMaterial(data.material));break;case"LOD":object=new THREE.LOD;break;case"Line":object=new THREE.Line(getGeometry(data.geometry),getMaterial(data.material),data.mode);break;case"PointCloud":case"Points":object=new THREE.Points(getGeometry(data.geometry),getMaterial(data.material));break;case"Sprite":object=new THREE.Sprite(getMaterial(data.material));break;case"Group":object=new THREE.Group;break;default:object=new THREE.Object3D}if(object.uuid=data.uuid,void 0!==data.name&&(object.name=data.name),void 0!==data.matrix?(matrix.fromArray(data.matrix),matrix.decompose(object.position,object.quaternion,object.scale)):(void 0!==data.position&&object.position.fromArray(data.position),void 0!==data.rotation&&object.rotation.fromArray(data.rotation),void 0!==data.scale&&object.scale.fromArray(data.scale)),void 0!==data.castShadow&&(object.castShadow=data.castShadow),void 0!==data.receiveShadow&&(object.receiveShadow=data.receiveShadow),void 0!==data.visible&&(object.visible=data.visible),void 0!==data.userData&&(object.userData=data.userData),void 0!==data.children)for(var child in data.children)object.add(this.parseObject(data.children[child],geometries,materials));if("LOD"===data.type)for(var levels=data.levels,l=0;li;++i)loadTexture(i);else loader.load(url,function(buffer){var texDatas=scope._parser(buffer,!0);if(texDatas.isCubemap)for(var faces=texDatas.mipmaps.length/texDatas.mipmapCount,f=0;faces>f;f++){images[f]={mipmaps:[]};for(var i=0;i0&&(data.alphaTest=this.alphaTest),this.wireframe===!0&&(data.wireframe=this.wireframe),this.wireframeLinewidth>1&&(data.wireframeLinewidth=this.wireframeLinewidth),data},clone:function(){return(new this.constructor).copy(this)},copy:function(source){return this.name=source.name,this.side=source.side,this.opacity=source.opacity,this.transparent=source.transparent,this.blending=source.blending,this.blendSrc=source.blendSrc,this.blendDst=source.blendDst,this.blendEquation=source.blendEquation,this.blendSrcAlpha=source.blendSrcAlpha,this.blendDstAlpha=source.blendDstAlpha,this.blendEquationAlpha=source.blendEquationAlpha,this.depthFunc=source.depthFunc,this.depthTest=source.depthTest,this.depthWrite=source.depthWrite,this.precision=source.precision,this.polygonOffset=source.polygonOffset,this.polygonOffsetFactor=source.polygonOffsetFactor,this.polygonOffsetUnits=source.polygonOffsetUnits,this.alphaTest=source.alphaTest,this.overdraw=source.overdraw,this.visible=source.visible,this},update:function(){this.dispatchEvent({type:"update"})},dispose:function(){this.dispatchEvent({type:"dispose"})},get wrapAround(){console.warn("THREE."+this.type+": .wrapAround has been removed.")},set wrapAround(boolean){console.warn("THREE."+this.type+": .wrapAround has been removed.")},get wrapRGB(){return console.warn("THREE."+this.type+": .wrapRGB has been removed."),new THREE.Color}},THREE.EventDispatcher.prototype.apply(THREE.Material.prototype),THREE.MaterialIdCount=0,THREE.LineBasicMaterial=function(parameters){THREE.Material.call(this),this.type="LineBasicMaterial",this.color=new THREE.Color(16777215),this.linewidth=1,this.linecap="round",this.linejoin="round",this.vertexColors=THREE.NoColors,this.fog=!0,this.setValues(parameters)},THREE.LineBasicMaterial.prototype=Object.create(THREE.Material.prototype),THREE.LineBasicMaterial.prototype.constructor=THREE.LineBasicMaterial,THREE.LineBasicMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.linewidth=source.linewidth,this.linecap=source.linecap,this.linejoin=source.linejoin,this.vertexColors=source.vertexColors,this.fog=source.fog,this},THREE.LineDashedMaterial=function(parameters){THREE.Material.call(this),this.type="LineDashedMaterial",this.color=new THREE.Color(16777215),this.linewidth=1,this.scale=1,this.dashSize=3,this.gapSize=1,this.vertexColors=!1,this.fog=!0,this.setValues(parameters)},THREE.LineDashedMaterial.prototype=Object.create(THREE.Material.prototype),THREE.LineDashedMaterial.prototype.constructor=THREE.LineDashedMaterial,THREE.LineDashedMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.linewidth=source.linewidth,this.scale=source.scale,this.dashSize=source.dashSize,this.gapSize=source.gapSize,this.vertexColors=source.vertexColors,this.fog=source.fog,this},THREE.MeshBasicMaterial=function(parameters){THREE.Material.call(this),this.type="MeshBasicMaterial",this.color=new THREE.Color(16777215),this.map=null,this.aoMap=null,this.aoMapIntensity=1,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.combine=THREE.MultiplyOperation,this.reflectivity=1,this.refractionRatio=.98,this.fog=!0,this.shading=THREE.SmoothShading,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.vertexColors=THREE.NoColors,this.skinning=!1,this.morphTargets=!1,this.setValues(parameters)},THREE.MeshBasicMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshBasicMaterial.prototype.constructor=THREE.MeshBasicMaterial,THREE.MeshBasicMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.map=source.map,this.aoMap=source.aoMap,this.aoMapIntensity=source.aoMapIntensity,this.specularMap=source.specularMap,this.alphaMap=source.alphaMap,this.envMap=source.envMap,this.combine=source.combine,this.reflectivity=source.reflectivity,this.refractionRatio=source.refractionRatio,this.fog=source.fog,this.shading=source.shading,this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this.wireframeLinecap=source.wireframeLinecap,this.wireframeLinejoin=source.wireframeLinejoin,this.vertexColors=source.vertexColors,this.skinning=source.skinning,this.morphTargets=source.morphTargets,this},THREE.MeshLambertMaterial=function(parameters){THREE.Material.call(this),this.type="MeshLambertMaterial",this.color=new THREE.Color(16777215),this.emissive=new THREE.Color(0),this.map=null,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.combine=THREE.MultiplyOperation,this.reflectivity=1,this.refractionRatio=.98,this.fog=!0,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.vertexColors=THREE.NoColors,this.skinning=!1,this.morphTargets=!1,this.morphNormals=!1,this.setValues(parameters)},THREE.MeshLambertMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshLambertMaterial.prototype.constructor=THREE.MeshLambertMaterial,THREE.MeshLambertMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.emissive.copy(source.emissive),this.map=source.map,this.specularMap=source.specularMap,this.alphaMap=source.alphaMap,this.envMap=source.envMap,this.combine=source.combine,this.reflectivity=source.reflectivity,this.refractionRatio=source.refractionRatio,this.fog=source.fog,this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this.wireframeLinecap=source.wireframeLinecap,this.wireframeLinejoin=source.wireframeLinejoin,this.vertexColors=source.vertexColors,this.skinning=source.skinning,this.morphTargets=source.morphTargets,this.morphNormals=source.morphNormals,this},THREE.MeshPhongMaterial=function(parameters){THREE.Material.call(this),this.type="MeshPhongMaterial",this.color=new THREE.Color(16777215),this.emissive=new THREE.Color(0),this.specular=new THREE.Color(1118481),this.shininess=30,this.metal=!1,this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalScale=new THREE.Vector2(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.combine=THREE.MultiplyOperation,this.reflectivity=1,this.refractionRatio=.98,this.fog=!0,this.shading=THREE.SmoothShading,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.vertexColors=THREE.NoColors,this.skinning=!1,this.morphTargets=!1,this.morphNormals=!1,this.setValues(parameters)},THREE.MeshPhongMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshPhongMaterial.prototype.constructor=THREE.MeshPhongMaterial,THREE.MeshPhongMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.emissive.copy(source.emissive),this.specular.copy(source.specular),this.shininess=source.shininess,this.metal=source.metal,this.map=source.map,this.lightMap=source.lightMap,this.lightMapIntensity=source.lightMapIntensity,this.aoMap=source.aoMap,this.aoMapIntensity=source.aoMapIntensity,this.emissiveMap=source.emissiveMap,this.bumpMap=source.bumpMap,this.bumpScale=source.bumpScale,this.normalMap=source.normalMap,this.normalScale.copy(source.normalScale),this.displacementMap=source.displacementMap,this.displacementScale=source.displacementScale,this.displacementBias=source.displacementBias,this.specularMap=source.specularMap,this.alphaMap=source.alphaMap,this.envMap=source.envMap,this.combine=source.combine,this.reflectivity=source.reflectivity,this.refractionRatio=source.refractionRatio,this.fog=source.fog,this.shading=source.shading,this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this.wireframeLinecap=source.wireframeLinecap,this.wireframeLinejoin=source.wireframeLinejoin,this.vertexColors=source.vertexColors,this.skinning=source.skinning,this.morphTargets=source.morphTargets,this.morphNormals=source.morphNormals,this},THREE.MeshDepthMaterial=function(parameters){THREE.Material.call(this),this.type="MeshDepthMaterial",this.morphTargets=!1,this.wireframe=!1,this.wireframeLinewidth=1,this.setValues(parameters)},THREE.MeshDepthMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshDepthMaterial.prototype.constructor=THREE.MeshDepthMaterial,THREE.MeshDepthMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this},THREE.MeshNormalMaterial=function(parameters){THREE.Material.call(this,parameters),this.type="MeshNormalMaterial",this.wireframe=!1,this.wireframeLinewidth=1,this.morphTargets=!1,this.setValues(parameters)},THREE.MeshNormalMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshNormalMaterial.prototype.constructor=THREE.MeshNormalMaterial,THREE.MeshNormalMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this},THREE.MultiMaterial=function(materials){this.uuid=THREE.Math.generateUUID(),this.type="MultiMaterial",this.materials=materials instanceof Array?materials:[],this.visible=!0},THREE.MultiMaterial.prototype={constructor:THREE.MultiMaterial,toJSON:function(){for(var output={metadata:{version:4.2,type:"material",generator:"MaterialExporter"},uuid:this.uuid,type:this.type,materials:[]},i=0,l=this.materials.length;l>i;i++)output.materials.push(this.materials[i].toJSON());return output.visible=this.visible,output},clone:function(){for(var material=new this.constructor,i=0;i2048||canvas.height>2048?canvas.toDataURL("image/jpeg",.6):canvas.toDataURL("image/png")}if(void 0!==meta.textures[this.uuid])return meta.textures[this.uuid];var output={metadata:{version:4.4,type:"Texture",generator:"Texture.toJSON"},uuid:this.uuid,name:this.name,mapping:this.mapping,repeat:[this.repeat.x,this.repeat.y],offset:[this.offset.x,this.offset.y],wrap:[this.wrapS,this.wrapT],minFilter:this.minFilter,magFilter:this.magFilter,anisotropy:this.anisotropy};if(void 0!==this.image){var image=this.image;void 0===image.uuid&&(image.uuid=THREE.Math.generateUUID()),void 0===meta.images[image.uuid]&&(meta.images[image.uuid]={uuid:image.uuid,url:getDataURL(image)}),output.image=image.uuid}return meta.textures[this.uuid]=output,output},dispose:function(){this.dispatchEvent({type:"dispose"})},transformUv:function(uv){if(this.mapping===THREE.UVMapping){if(uv.multiply(this.repeat),uv.add(this.offset),uv.x<0||uv.x>1)switch(this.wrapS){case THREE.RepeatWrapping:uv.x=uv.x-Math.floor(uv.x);break;case THREE.ClampToEdgeWrapping:uv.x=uv.x<0?0:1;break;case THREE.MirroredRepeatWrapping:1===Math.abs(Math.floor(uv.x)%2)?uv.x=Math.ceil(uv.x)-uv.x:uv.x=uv.x-Math.floor(uv.x)}if(uv.y<0||uv.y>1)switch(this.wrapT){case THREE.RepeatWrapping:uv.y=uv.y-Math.floor(uv.y);break;case THREE.ClampToEdgeWrapping:uv.y=uv.y<0?0:1;break;case THREE.MirroredRepeatWrapping:1===Math.abs(Math.floor(uv.y)%2)?uv.y=Math.ceil(uv.y)-uv.y:uv.y=uv.y-Math.floor(uv.y)}this.flipY&&(uv.y=1-uv.y)}}},THREE.EventDispatcher.prototype.apply(THREE.Texture.prototype),THREE.TextureIdCount=0,THREE.CanvasTexture=function(canvas,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy){THREE.Texture.call(this,canvas,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.needsUpdate=!0},THREE.CanvasTexture.prototype=Object.create(THREE.Texture.prototype),THREE.CanvasTexture.prototype.constructor=THREE.CanvasTexture,THREE.CubeTexture=function(images,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy){mapping=void 0!==mapping?mapping:THREE.CubeReflectionMapping,THREE.Texture.call(this,images,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.images=images,this.flipY=!1},THREE.CubeTexture.prototype=Object.create(THREE.Texture.prototype),THREE.CubeTexture.prototype.constructor=THREE.CubeTexture,THREE.CubeTexture.prototype.copy=function(source){return THREE.Texture.prototype.copy.call(this,source),this.images=source.images,this},THREE.CompressedTexture=function(mipmaps,width,height,format,type,mapping,wrapS,wrapT,magFilter,minFilter,anisotropy){THREE.Texture.call(this,null,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.image={width:width,height:height},this.mipmaps=mipmaps,this.flipY=!1,this.generateMipmaps=!1},THREE.CompressedTexture.prototype=Object.create(THREE.Texture.prototype),THREE.CompressedTexture.prototype.constructor=THREE.CompressedTexture,THREE.DataTexture=function(data,width,height,format,type,mapping,wrapS,wrapT,magFilter,minFilter,anisotropy){THREE.Texture.call(this,null,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.image={data:data,width:width,height:height
+},this.magFilter=void 0!==magFilter?magFilter:THREE.NearestFilter,this.minFilter=void 0!==minFilter?minFilter:THREE.NearestFilter,this.flipY=!1,this.generateMipmaps=!1},THREE.DataTexture.prototype=Object.create(THREE.Texture.prototype),THREE.DataTexture.prototype.constructor=THREE.DataTexture,THREE.VideoTexture=function(video,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy){function update(){requestAnimationFrame(update),video.readyState===video.HAVE_ENOUGH_DATA&&(scope.needsUpdate=!0)}THREE.Texture.call(this,video,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.generateMipmaps=!1;var scope=this;update()},THREE.VideoTexture.prototype=Object.create(THREE.Texture.prototype),THREE.VideoTexture.prototype.constructor=THREE.VideoTexture,THREE.Group=function(){THREE.Object3D.call(this),this.type="Group"},THREE.Group.prototype=Object.create(THREE.Object3D.prototype),THREE.Group.prototype.constructor=THREE.Group,THREE.Points=function(geometry,material){THREE.Object3D.call(this),this.type="Points",this.geometry=void 0!==geometry?geometry:new THREE.Geometry,this.material=void 0!==material?material:new THREE.PointsMaterial({color:16777215*Math.random()})},THREE.Points.prototype=Object.create(THREE.Object3D.prototype),THREE.Points.prototype.constructor=THREE.Points,THREE.Points.prototype.raycast=function(){var inverseMatrix=new THREE.Matrix4,ray=new THREE.Ray;return function(raycaster,intersects){function testPoint(point,index){var rayPointDistanceSq=ray.distanceSqToPoint(point);if(localThresholdSq>rayPointDistanceSq){var intersectPoint=ray.closestPointToPoint(point);intersectPoint.applyMatrix4(object.matrixWorld);var distance=raycaster.ray.origin.distanceTo(intersectPoint);if(distanceraycaster.far)return;intersects.push({distance:distance,distanceToRay:Math.sqrt(rayPointDistanceSq),point:intersectPoint.clone(),index:index,face:null,object:object})}}var object=this,geometry=object.geometry,threshold=raycaster.params.Points.threshold;if(inverseMatrix.getInverse(this.matrixWorld),ray.copy(raycaster.ray).applyMatrix4(inverseMatrix),null===geometry.boundingBox||ray.isIntersectionBox(geometry.boundingBox)!==!1){var localThreshold=threshold/((this.scale.x+this.scale.y+this.scale.z)/3),localThresholdSq=localThreshold*localThreshold,position=new THREE.Vector3;if(geometry instanceof THREE.BufferGeometry){var index=geometry.index,attributes=geometry.attributes,positions=attributes.position.array;if(null!==index)for(var indices=index.array,i=0,il=indices.length;il>i;i++){var a=indices[i];position.fromArray(positions,3*a),testPoint(position,a)}else for(var i=0,l=positions.length/3;l>i;i++)position.fromArray(positions,3*i),testPoint(position,i)}else for(var vertices=geometry.vertices,i=0,l=vertices.length;l>i;i++)testPoint(vertices[i],i)}}}(),THREE.Points.prototype.clone=function(){return new this.constructor(this.geometry,this.material).copy(this)},THREE.PointCloud=function(geometry,material){return console.warn("THREE.PointCloud has been renamed to THREE.Points."),new THREE.Points(geometry,material)},THREE.ParticleSystem=function(geometry,material){return console.warn("THREE.ParticleSystem has been renamed to THREE.Points."),new THREE.Points(geometry,material)},THREE.Line=function(geometry,material,mode){return 1===mode?(console.warn("THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead."),new THREE.LineSegments(geometry,material)):(THREE.Object3D.call(this),this.type="Line",this.geometry=void 0!==geometry?geometry:new THREE.Geometry,void(this.material=void 0!==material?material:new THREE.LineBasicMaterial({color:16777215*Math.random()})))},THREE.Line.prototype=Object.create(THREE.Object3D.prototype),THREE.Line.prototype.constructor=THREE.Line,THREE.Line.prototype.raycast=function(){var inverseMatrix=new THREE.Matrix4,ray=new THREE.Ray,sphere=new THREE.Sphere;return function(raycaster,intersects){var precision=raycaster.linePrecision,precisionSq=precision*precision,geometry=this.geometry;if(null===geometry.boundingSphere&&geometry.computeBoundingSphere(),sphere.copy(geometry.boundingSphere),sphere.applyMatrix4(this.matrixWorld),raycaster.ray.isIntersectionSphere(sphere)!==!1){inverseMatrix.getInverse(this.matrixWorld),ray.copy(raycaster.ray).applyMatrix4(inverseMatrix);var vStart=new THREE.Vector3,vEnd=new THREE.Vector3,interSegment=new THREE.Vector3,interRay=new THREE.Vector3,step=this instanceof THREE.LineSegments?2:1;if(geometry instanceof THREE.BufferGeometry){var index=geometry.index,attributes=geometry.attributes;if(null!==index)for(var indices=index.array,positions=attributes.position.array,i=0,l=indices.length-1;l>i;i+=step){var a=indices[i],b=indices[i+1];vStart.fromArray(positions,3*a),vEnd.fromArray(positions,3*b);var distSq=ray.distanceSqToSegment(vStart,vEnd,interRay,interSegment);if(!(distSq>precisionSq)){interRay.applyMatrix4(this.matrixWorld);var distance=raycaster.ray.origin.distanceTo(interRay);distanceraycaster.far||intersects.push({distance:distance,point:interSegment.clone().applyMatrix4(this.matrixWorld),index:i,face:null,faceIndex:null,object:this})}}else for(var positions=attributes.position.array,i=0,l=positions.length/3-1;l>i;i+=step){vStart.fromArray(positions,3*i),vEnd.fromArray(positions,3*i+3);var distSq=ray.distanceSqToSegment(vStart,vEnd,interRay,interSegment);if(!(distSq>precisionSq)){interRay.applyMatrix4(this.matrixWorld);var distance=raycaster.ray.origin.distanceTo(interRay);distanceraycaster.far||intersects.push({distance:distance,point:interSegment.clone().applyMatrix4(this.matrixWorld),index:i,face:null,faceIndex:null,object:this})}}}else if(geometry instanceof THREE.Geometry)for(var vertices=geometry.vertices,nbVertices=vertices.length,i=0;nbVertices-1>i;i+=step){var distSq=ray.distanceSqToSegment(vertices[i],vertices[i+1],interRay,interSegment);if(!(distSq>precisionSq)){interRay.applyMatrix4(this.matrixWorld);var distance=raycaster.ray.origin.distanceTo(interRay);distanceraycaster.far||intersects.push({distance:distance,point:interSegment.clone().applyMatrix4(this.matrixWorld),index:i,face:null,faceIndex:null,object:this})}}}}}(),THREE.Line.prototype.clone=function(){return new this.constructor(this.geometry,this.material).copy(this)},THREE.LineStrip=0,THREE.LinePieces=1,THREE.LineSegments=function(geometry,material){THREE.Line.call(this,geometry,material),this.type="LineSegments"},THREE.LineSegments.prototype=Object.create(THREE.Line.prototype),THREE.LineSegments.prototype.constructor=THREE.LineSegments,THREE.Mesh=function(geometry,material){THREE.Object3D.call(this),this.type="Mesh",this.geometry=void 0!==geometry?geometry:new THREE.Geometry,this.material=void 0!==material?material:new THREE.MeshBasicMaterial({color:16777215*Math.random()}),this.updateMorphTargets()},THREE.Mesh.prototype=Object.create(THREE.Object3D.prototype),THREE.Mesh.prototype.constructor=THREE.Mesh,THREE.Mesh.prototype.updateMorphTargets=function(){if(void 0!==this.geometry.morphTargets&&this.geometry.morphTargets.length>0){this.morphTargetBase=-1,this.morphTargetInfluences=[],this.morphTargetDictionary={};for(var m=0,ml=this.geometry.morphTargets.length;ml>m;m++)this.morphTargetInfluences.push(0),this.morphTargetDictionary[this.geometry.morphTargets[m].name]=m}},THREE.Mesh.prototype.getMorphTargetIndexByName=function(name){return void 0!==this.morphTargetDictionary[name]?this.morphTargetDictionary[name]:(console.warn("THREE.Mesh.getMorphTargetIndexByName: morph target "+name+" does not exist. Returning 0."),0)},THREE.Mesh.prototype.raycast=function(){function uvIntersection(point,p1,p2,p3,uv1,uv2,uv3){return THREE.Triangle.barycoordFromPoint(point,p1,p2,p3,barycoord),uv1.multiplyScalar(barycoord.x),uv2.multiplyScalar(barycoord.y),uv3.multiplyScalar(barycoord.z),uv1.add(uv2).add(uv3),uv1.clone()}function checkIntersection(object,raycaster,ray,pA,pB,pC,point){var intersect,material=object.material;if(intersect=material.side===THREE.BackSide?ray.intersectTriangle(pC,pB,pA,!0,point):ray.intersectTriangle(pA,pB,pC,material.side!==THREE.DoubleSide,point),null===intersect)return null;intersectionPointWorld.copy(point),intersectionPointWorld.applyMatrix4(object.matrixWorld);var distance=raycaster.ray.origin.distanceTo(intersectionPointWorld);return distanceraycaster.far?null:{distance:distance,point:intersectionPointWorld.clone(),object:object}}function checkBufferGeometryIntersection(object,raycaster,ray,positions,uvs,a,b,c){vA.fromArray(positions,3*a),vB.fromArray(positions,3*b),vC.fromArray(positions,3*c);var intersection=checkIntersection(object,raycaster,ray,vA,vB,vC,intersectionPoint);return intersection&&(uvs&&(uvA.fromArray(uvs,2*a),uvB.fromArray(uvs,2*b),uvC.fromArray(uvs,2*c),intersection.uv=uvIntersection(intersectionPoint,vA,vB,vC,uvA,uvB,uvC)),intersection.face=new THREE.Face3(a,b,c,THREE.Triangle.normal(vA,vB,vC)),intersection.faceIndex=a),intersection}var inverseMatrix=new THREE.Matrix4,ray=new THREE.Ray,sphere=new THREE.Sphere,vA=new THREE.Vector3,vB=new THREE.Vector3,vC=new THREE.Vector3,tempA=new THREE.Vector3,tempB=new THREE.Vector3,tempC=new THREE.Vector3,uvA=new THREE.Vector2,uvB=new THREE.Vector2,uvC=new THREE.Vector2,barycoord=new THREE.Vector3,intersectionPoint=new THREE.Vector3,intersectionPointWorld=new THREE.Vector3;return function(raycaster,intersects){var geometry=this.geometry,material=this.material;if(void 0!==material){null===geometry.boundingSphere&&geometry.computeBoundingSphere();var matrixWorld=this.matrixWorld;if(sphere.copy(geometry.boundingSphere),sphere.applyMatrix4(matrixWorld),raycaster.ray.isIntersectionSphere(sphere)!==!1&&(inverseMatrix.getInverse(matrixWorld),ray.copy(raycaster.ray).applyMatrix4(inverseMatrix),null===geometry.boundingBox||ray.isIntersectionBox(geometry.boundingBox)!==!1)){var uvs,intersection;if(geometry instanceof THREE.BufferGeometry){var a,b,c,index=geometry.index,attributes=geometry.attributes,positions=attributes.position.array;if(void 0!==attributes.uv&&(uvs=attributes.uv.array),null!==index)for(var indices=index.array,i=0,l=indices.length;l>i;i+=3)a=indices[i],b=indices[i+1],c=indices[i+2],intersection=checkBufferGeometryIntersection(this,raycaster,ray,positions,uvs,a,b,c),intersection&&(intersection.faceIndex=Math.floor(i/3),intersects.push(intersection));else for(var i=0,l=positions.length;l>i;i+=9)a=i/3,b=a+1,c=a+2,intersection=checkBufferGeometryIntersection(this,raycaster,ray,positions,uvs,a,b,c),intersection&&(intersection.index=a,intersects.push(intersection))}else if(geometry instanceof THREE.Geometry){var fvA,fvB,fvC,isFaceMaterial=material instanceof THREE.MeshFaceMaterial,materials=isFaceMaterial===!0?material.materials:null,vertices=geometry.vertices,faces=geometry.faces,faceVertexUvs=geometry.faceVertexUvs[0];faceVertexUvs.length>0&&(uvs=faceVertexUvs);for(var f=0,fl=faces.length;fl>f;f++){var face=faces[f],faceMaterial=isFaceMaterial===!0?materials[face.materialIndex]:material;if(void 0!==faceMaterial){if(fvA=vertices[face.a],fvB=vertices[face.b],fvC=vertices[face.c],faceMaterial.morphTargets===!0){var morphTargets=geometry.morphTargets,morphInfluences=this.morphTargetInfluences;vA.set(0,0,0),vB.set(0,0,0),vC.set(0,0,0);for(var t=0,tl=morphTargets.length;tl>t;t++){var influence=morphInfluences[t];if(0!==influence){var targets=morphTargets[t].vertices;vA.addScaledVector(tempA.subVectors(targets[face.a],fvA),influence),vB.addScaledVector(tempB.subVectors(targets[face.b],fvB),influence),vC.addScaledVector(tempC.subVectors(targets[face.c],fvC),influence)}}vA.add(fvA),vB.add(fvB),vC.add(fvC),fvA=vA,fvB=vB,fvC=vC}if(intersection=checkIntersection(this,raycaster,ray,fvA,fvB,fvC,intersectionPoint)){if(uvs){var uvs_f=uvs[f];uvA.copy(uvs_f[0]),uvB.copy(uvs_f[1]),uvC.copy(uvs_f[2]),intersection.uv=uvIntersection(intersectionPoint,fvA,fvB,fvC,uvA,uvB,uvC)}intersection.face=face,intersection.faceIndex=f,intersects.push(intersection)}}}}}}}}(),THREE.Mesh.prototype.clone=function(){return new this.constructor(this.geometry,this.material).copy(this)},THREE.Bone=function(skin){THREE.Object3D.call(this),this.type="Bone",this.skin=skin},THREE.Bone.prototype=Object.create(THREE.Object3D.prototype),THREE.Bone.prototype.constructor=THREE.Bone,THREE.Bone.prototype.copy=function(source){return THREE.Object3D.prototype.copy.call(this,source),this.skin=source.skin,this},THREE.Skeleton=function(bones,boneInverses,useVertexTexture){if(this.useVertexTexture=void 0!==useVertexTexture?useVertexTexture:!0,this.identityMatrix=new THREE.Matrix4,bones=bones||[],this.bones=bones.slice(0),this.useVertexTexture){var size=Math.sqrt(4*this.bones.length);size=THREE.Math.nextPowerOfTwo(Math.ceil(size)),size=Math.max(size,4),this.boneTextureWidth=size,this.boneTextureHeight=size,this.boneMatrices=new Float32Array(this.boneTextureWidth*this.boneTextureHeight*4),this.boneTexture=new THREE.DataTexture(this.boneMatrices,this.boneTextureWidth,this.boneTextureHeight,THREE.RGBAFormat,THREE.FloatType)}else this.boneMatrices=new Float32Array(16*this.bones.length);if(void 0===boneInverses)this.calculateInverses();else if(this.bones.length===boneInverses.length)this.boneInverses=boneInverses.slice(0);else{console.warn("THREE.Skeleton bonInverses is the wrong length."),this.boneInverses=[];for(var b=0,bl=this.bones.length;bl>b;b++)this.boneInverses.push(new THREE.Matrix4)}},THREE.Skeleton.prototype.calculateInverses=function(){this.boneInverses=[];for(var b=0,bl=this.bones.length;bl>b;b++){var inverse=new THREE.Matrix4;this.bones[b]&&inverse.getInverse(this.bones[b].matrixWorld),this.boneInverses.push(inverse)}},THREE.Skeleton.prototype.pose=function(){for(var bone,b=0,bl=this.bones.length;bl>b;b++)bone=this.bones[b],bone&&bone.matrixWorld.getInverse(this.boneInverses[b]);for(var b=0,bl=this.bones.length;bl>b;b++)bone=this.bones[b],bone&&(bone.parent?(bone.matrix.getInverse(bone.parent.matrixWorld),bone.matrix.multiply(bone.matrixWorld)):bone.matrix.copy(bone.matrixWorld),bone.matrix.decompose(bone.position,bone.quaternion,bone.scale))},THREE.Skeleton.prototype.update=function(){var offsetMatrix=new THREE.Matrix4;return function(){for(var b=0,bl=this.bones.length;bl>b;b++){var matrix=this.bones[b]?this.bones[b].matrixWorld:this.identityMatrix;offsetMatrix.multiplyMatrices(matrix,this.boneInverses[b]),offsetMatrix.flattenToArrayOffset(this.boneMatrices,16*b)}this.useVertexTexture&&(this.boneTexture.needsUpdate=!0)}}(),THREE.Skeleton.prototype.clone=function(){return new THREE.Skeleton(this.bones,this.boneInverses,this.useVertexTexture)},THREE.SkinnedMesh=function(geometry,material,useVertexTexture){THREE.Mesh.call(this,geometry,material),this.type="SkinnedMesh",this.bindMode="attached",this.bindMatrix=new THREE.Matrix4,this.bindMatrixInverse=new THREE.Matrix4;var bones=[];if(this.geometry&&void 0!==this.geometry.bones){for(var bone,gbone,b=0,bl=this.geometry.bones.length;bl>b;++b)gbone=this.geometry.bones[b],bone=new THREE.Bone(this),bones.push(bone),bone.name=gbone.name,bone.position.fromArray(gbone.pos),bone.quaternion.fromArray(gbone.rotq),void 0!==gbone.scl&&bone.scale.fromArray(gbone.scl);for(var b=0,bl=this.geometry.bones.length;bl>b;++b)gbone=this.geometry.bones[b],-1!==gbone.parent&&null!==gbone.parent?bones[gbone.parent].add(bones[b]):this.add(bones[b])}this.normalizeSkinWeights(),this.updateMatrixWorld(!0),this.bind(new THREE.Skeleton(bones,void 0,useVertexTexture),this.matrixWorld)},THREE.SkinnedMesh.prototype=Object.create(THREE.Mesh.prototype),THREE.SkinnedMesh.prototype.constructor=THREE.SkinnedMesh,THREE.SkinnedMesh.prototype.bind=function(skeleton,bindMatrix){this.skeleton=skeleton,void 0===bindMatrix&&(this.updateMatrixWorld(!0),this.skeleton.calculateInverses(),bindMatrix=this.matrixWorld),this.bindMatrix.copy(bindMatrix),this.bindMatrixInverse.getInverse(bindMatrix)},THREE.SkinnedMesh.prototype.pose=function(){this.skeleton.pose()},THREE.SkinnedMesh.prototype.normalizeSkinWeights=function(){if(this.geometry instanceof THREE.Geometry)for(var i=0;ii&&!(distance1){v1.setFromMatrixPosition(camera.matrixWorld),v2.setFromMatrixPosition(this.matrixWorld);var distance=v1.distanceTo(v2);levels[0].object.visible=!0;for(var i=1,l=levels.length;l>i&&distance>=levels[i].distance;i++)levels[i-1].object.visible=!1,levels[i].object.visible=!0;for(;l>i;i++)levels[i].object.visible=!1}}}(),THREE.LOD.prototype.copy=function(source){THREE.Object3D.prototype.copy.call(this,source,!1);for(var levels=source.levels,i=0,l=levels.length;l>i;i++){var level=levels[i];this.addLevel(level.object.clone(),level.distance)}return this},THREE.LOD.prototype.toJSON=function(meta){var data=THREE.Object3D.prototype.toJSON.call(this,meta);data.object.levels=[];for(var levels=this.levels,i=0,l=levels.length;l>i;i++){var level=levels[i];data.object.levels.push({object:level.object.uuid,distance:level.distance})}return data},THREE.Sprite=function(){var indices=new Uint16Array([0,1,2,0,2,3]),vertices=new Float32Array([-.5,-.5,0,.5,-.5,0,.5,.5,0,-.5,.5,0]),uvs=new Float32Array([0,0,1,0,1,1,0,1]),geometry=new THREE.BufferGeometry;return geometry.setIndex(new THREE.BufferAttribute(indices,1)),geometry.addAttribute("position",new THREE.BufferAttribute(vertices,3)),geometry.addAttribute("uv",new THREE.BufferAttribute(uvs,2)),function(material){THREE.Object3D.call(this),this.type="Sprite",this.geometry=geometry,this.material=void 0!==material?material:new THREE.SpriteMaterial}}(),THREE.Sprite.prototype=Object.create(THREE.Object3D.prototype),THREE.Sprite.prototype.constructor=THREE.Sprite,THREE.Sprite.prototype.raycast=function(){var matrixPosition=new THREE.Vector3;return function(raycaster,intersects){matrixPosition.setFromMatrixPosition(this.matrixWorld);var distanceSq=raycaster.ray.distanceSqToPoint(matrixPosition),guessSizeSq=this.scale.x*this.scale.y;distanceSq>guessSizeSq||intersects.push({distance:Math.sqrt(distanceSq),point:this.position,face:null,object:this})}}(),THREE.Sprite.prototype.clone=function(){return new this.constructor(this.material).copy(this)},THREE.Particle=THREE.Sprite,THREE.LensFlare=function(texture,size,distance,blending,color){THREE.Object3D.call(this),this.lensFlares=[],this.positionScreen=new THREE.Vector3,this.customUpdateCallback=void 0,void 0!==texture&&this.add(texture,size,distance,blending,color)},THREE.LensFlare.prototype=Object.create(THREE.Object3D.prototype),THREE.LensFlare.prototype.constructor=THREE.LensFlare,THREE.LensFlare.prototype.add=function(texture,size,distance,blending,color,opacity){void 0===size&&(size=-1),void 0===distance&&(distance=0),void 0===opacity&&(opacity=1),void 0===color&&(color=new THREE.Color(16777215)),void 0===blending&&(blending=THREE.NormalBlending),distance=Math.min(distance,Math.max(0,distance)),this.lensFlares.push({texture:texture,size:size,distance:distance,x:0,y:0,z:0,scale:1,rotation:0,opacity:opacity,color:color,blending:blending})},THREE.LensFlare.prototype.updateLensFlares=function(){var f,flare,fl=this.lensFlares.length,vecX=2*-this.positionScreen.x,vecY=2*-this.positionScreen.y;for(f=0;fl>f;f++)flare=this.lensFlares[f],flare.x=this.positionScreen.x+vecX*flare.distance,flare.y=this.positionScreen.y+vecY*flare.distance,flare.wantedRotation=flare.x*Math.PI*.25,flare.rotation+=.25*(flare.wantedRotation-flare.rotation)},THREE.LensFlare.prototype.copy=function(source){THREE.Object3D.prototype.copy.call(this,source),this.positionScreen.copy(source.positionScreen),this.customUpdateCallback=source.customUpdateCallback;for(var i=0,l=source.lensFlares.length;l>i;i++)this.lensFlares.push(source.lensFlares[i]);return this},THREE.Scene=function(){THREE.Object3D.call(this),this.type="Scene",this.fog=null,this.overrideMaterial=null,this.autoUpdate=!0},THREE.Scene.prototype=Object.create(THREE.Object3D.prototype),THREE.Scene.prototype.constructor=THREE.Scene,THREE.Scene.prototype.copy=function(source){return THREE.Object3D.prototype.copy.call(this,source),null!==source.fog&&(this.fog=source.fog.clone()),null!==source.overrideMaterial&&(this.overrideMaterial=source.overrideMaterial.clone()),this.autoUpdate=source.autoUpdate,this.matrixAutoUpdate=source.matrixAutoUpdate,this},THREE.Fog=function(color,near,far){this.name="",this.color=new THREE.Color(color),this.near=void 0!==near?near:1,this.far=void 0!==far?far:1e3},THREE.Fog.prototype.clone=function(){return new THREE.Fog(this.color.getHex(),this.near,this.far)},THREE.FogExp2=function(color,density){this.name="",this.color=new THREE.Color(color),this.density=void 0!==density?density:25e-5},THREE.FogExp2.prototype.clone=function(){return new THREE.FogExp2(this.color.getHex(),this.density)},THREE.ShaderChunk={},THREE.ShaderChunk.alphamap_fragment="#ifdef USE_ALPHAMAP\n\n diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n",THREE.ShaderChunk.alphamap_pars_fragment="#ifdef USE_ALPHAMAP\n\n uniform sampler2D alphaMap;\n\n#endif\n",THREE.ShaderChunk.alphatest_fragment="#ifdef ALPHATEST\n\n if ( diffuseColor.a < ALPHATEST ) discard;\n\n#endif\n",THREE.ShaderChunk.aomap_fragment="#ifdef USE_AOMAP\n\n totalAmbientLight *= ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\n#endif\n",THREE.ShaderChunk.aomap_pars_fragment="#ifdef USE_AOMAP\n\n uniform sampler2D aoMap;\n uniform float aoMapIntensity;\n\n#endif",THREE.ShaderChunk.begin_vertex="\nvec3 transformed = vec3( position );\n",THREE.ShaderChunk.beginnormal_vertex="\nvec3 objectNormal = vec3( normal );\n",THREE.ShaderChunk.bumpmap_pars_fragment="#ifdef USE_BUMPMAP\n\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n\n\n\n vec2 dHdxy_fwd() {\n\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\n\n float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n return vec2( dBx, dBy );\n\n }\n\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm;\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n\n float fDet = dot( vSigmaX, R1 );\n\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n\n }\n\n#endif\n",THREE.ShaderChunk.color_fragment="#ifdef USE_COLOR\n\n diffuseColor.rgb *= vColor;\n\n#endif",THREE.ShaderChunk.color_pars_fragment="#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif\n",THREE.ShaderChunk.color_pars_vertex="#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif",THREE.ShaderChunk.color_vertex="#ifdef USE_COLOR\n\n vColor.xyz = color.xyz;\n\n#endif",THREE.ShaderChunk.common="#define PI 3.14159\n#define PI2 6.28318\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\n\nvec3 transformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz );\n\n}\n\nvec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );\n\n}\n\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n float distance = dot( planeNormal, point - pointOnPlane );\n\n return - distance * planeNormal + point;\n\n}\n\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return sign( dot( point - pointOnPlane, planeNormal ) );\n\n}\n\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n\n}\n\nfloat calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {\n\n if ( decayExponent > 0.0 ) {\n\n return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\n }\n\n return 1.0;\n\n}\n\nvec3 F_Schlick( in vec3 specularColor, in float dotLH ) {\n\n\n float fresnel = exp2( ( -5.55437 * dotLH - 6.98316 ) * dotLH );\n\n return ( 1.0 - specularColor ) * fresnel + specularColor;\n\n}\n\nfloat G_BlinnPhong_Implicit( /* in float dotNL, in float dotNV */ ) {\n\n\n return 0.25;\n\n}\n\nfloat D_BlinnPhong( in float shininess, in float dotNH ) {\n\n\n return ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n\n}\n\nvec3 BRDF_BlinnPhong( in vec3 specularColor, in float shininess, in vec3 normal, in vec3 lightDir, in vec3 viewDir ) {\n\n vec3 halfDir = normalize( lightDir + viewDir );\n\n float dotNH = saturate( dot( normal, halfDir ) );\n float dotLH = saturate( dot( lightDir, halfDir ) );\n\n vec3 F = F_Schlick( specularColor, dotLH );\n\n float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );\n\n float D = D_BlinnPhong( shininess, dotNH );\n\n return F * G * D;\n\n}\n\nvec3 inputToLinear( in vec3 a ) {\n\n #ifdef GAMMA_INPUT\n\n return pow( a, vec3( float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n\nvec3 linearToOutput( in vec3 a ) {\n\n #ifdef GAMMA_OUTPUT\n\n return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n",THREE.ShaderChunk.defaultnormal_vertex="#ifdef FLIP_SIDED\n\n objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;\n",THREE.ShaderChunk.displacementmap_vertex="#ifdef USE_DISPLACEMENTMAP\n\n transformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n\n#endif\n",THREE.ShaderChunk.displacementmap_pars_vertex="#ifdef USE_DISPLACEMENTMAP\n\n uniform sampler2D displacementMap;\n uniform float displacementScale;\n uniform float displacementBias;\n\n#endif\n",THREE.ShaderChunk.emissivemap_fragment="#ifdef USE_EMISSIVEMAP\n\n vec4 emissiveColor = texture2D( emissiveMap, vUv );\n\n emissiveColor.rgb = inputToLinear( emissiveColor.rgb );\n\n totalEmissiveLight *= emissiveColor.rgb;\n\n#endif\n",THREE.ShaderChunk.emissivemap_pars_fragment="#ifdef USE_EMISSIVEMAP\n\n uniform sampler2D emissiveMap;\n\n#endif\n",THREE.ShaderChunk.envmap_fragment="#ifdef USE_ENVMAP\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n #else\n\n vec3 reflectVec = vReflect;\n\n #endif\n\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n\n #ifdef ENVMAP_TYPE_CUBE\n vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n vec4 envColor = texture2D( envMap, sampleUV );\n\n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n #endif\n\n envColor.xyz = inputToLinear( envColor.xyz );\n\n #ifdef ENVMAP_BLENDING_MULTIPLY\n\n outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_MIX )\n\n outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_ADD )\n\n outgoingLight += envColor.xyz * specularStrength * reflectivity;\n\n #endif\n\n#endif\n",THREE.ShaderChunk.envmap_pars_fragment="#ifdef USE_ENVMAP\n\n uniform float reflectivity;\n #ifdef ENVMAP_TYPE_CUBE\n uniform samplerCube envMap;\n #else\n uniform sampler2D envMap;\n #endif\n uniform float flipEnvMap;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n uniform float refractionRatio;\n\n #else\n\n varying vec3 vReflect;\n\n #endif\n\n#endif\n",THREE.ShaderChunk.envmap_pars_vertex="#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n varying vec3 vReflect;\n\n uniform float refractionRatio;\n\n#endif\n",THREE.ShaderChunk.envmap_vertex="#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vReflect = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n#endif\n",THREE.ShaderChunk.fog_fragment="#ifdef USE_FOG\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n #else\n\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #endif\n\n #ifdef FOG_EXP2\n\n float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n\n #else\n\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n #endif\n \n outgoingLight = mix( outgoingLight, fogColor, fogFactor );\n\n#endif",THREE.ShaderChunk.fog_pars_fragment="#ifdef USE_FOG\n\n uniform vec3 fogColor;\n\n #ifdef FOG_EXP2\n\n uniform float fogDensity;\n\n #else\n\n uniform float fogNear;\n uniform float fogFar;\n #endif\n\n#endif",THREE.ShaderChunk.hemilight_fragment="#if MAX_HEMI_LIGHTS > 0\n\n for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vec3 lightColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n totalAmbientLight += lightColor;\n\n }\n\n#endif\n\n",
+THREE.ShaderChunk.lightmap_fragment="#ifdef USE_LIGHTMAP\n\n totalAmbientLight += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\n#endif\n",THREE.ShaderChunk.lightmap_pars_fragment="#ifdef USE_LIGHTMAP\n\n uniform sampler2D lightMap;\n uniform float lightMapIntensity;\n\n#endif",THREE.ShaderChunk.lights_lambert_pars_vertex="#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n",THREE.ShaderChunk.lights_lambert_vertex="vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vec3( 0.0 );\n\n#endif\n\nvec3 normal = normalize( transformedNormal );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lVector = pointLightPosition[ i ] - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * saturate( - dotProduct );\n\n #endif\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n #ifdef DOUBLE_SIDED\n\n float hemiDiffuseWeightBack = - 0.5 * dotProduct + 0.5;\n\n vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n #endif\n\n }\n\n#endif\n",THREE.ShaderChunk.lights_phong_fragment="vec3 viewDir = normalize( vViewPosition );\n\nvec3 totalDiffuseLight = vec3( 0.0 );\nvec3 totalSpecularLight = vec3( 0.0 );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lightPosition = pointLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * cosineTerm;\n\n }\n\n#endif\n",THREE.ShaderChunk.lights_phong_pars_fragment="uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n\n varying vec3 vNormal;\n\n#endif\n",THREE.ShaderChunk.lights_phong_pars_vertex="#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\n#endif\n",THREE.ShaderChunk.lights_phong_vertex="#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n vWorldPosition = worldPosition.xyz;\n\n#endif\n",THREE.ShaderChunk.linear_to_gamma_fragment="\n outgoingLight = linearToOutput( outgoingLight );\n",THREE.ShaderChunk.logdepthbuf_fragment="#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif",THREE.ShaderChunk.logdepthbuf_pars_fragment="#ifdef USE_LOGDEPTHBUF\n\n uniform float logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n#endif\n",THREE.ShaderChunk.logdepthbuf_pars_vertex="#ifdef USE_LOGDEPTHBUF\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n uniform float logDepthBufFC;\n\n#endif",THREE.ShaderChunk.logdepthbuf_vertex="#ifdef USE_LOGDEPTHBUF\n\n gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n vFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n #endif\n\n#endif",THREE.ShaderChunk.map_fragment="#ifdef USE_MAP\n\n vec4 texelColor = texture2D( map, vUv );\n\n texelColor.xyz = inputToLinear( texelColor.xyz );\n\n diffuseColor *= texelColor;\n\n#endif\n",THREE.ShaderChunk.map_pars_fragment="#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif",THREE.ShaderChunk.map_particle_fragment="#ifdef USE_MAP\n\n diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\n#endif\n",THREE.ShaderChunk.map_particle_pars_fragment="#ifdef USE_MAP\n\n uniform vec4 offsetRepeat;\n uniform sampler2D map;\n\n#endif\n",THREE.ShaderChunk.morphnormal_vertex="#ifdef USE_MORPHNORMALS\n\n objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n#endif\n",THREE.ShaderChunk.morphtarget_pars_vertex="#ifdef USE_MORPHTARGETS\n\n #ifndef USE_MORPHNORMALS\n\n uniform float morphTargetInfluences[ 8 ];\n\n #else\n\n uniform float morphTargetInfluences[ 4 ];\n\n #endif\n\n#endif",THREE.ShaderChunk.morphtarget_vertex="#ifdef USE_MORPHTARGETS\n\n transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n #ifndef USE_MORPHNORMALS\n\n transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n #endif\n\n#endif\n",THREE.ShaderChunk.normal_phong_fragment="#ifndef FLAT_SHADED\n\n vec3 normal = normalize( vNormal );\n\n #ifdef DOUBLE_SIDED\n\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n #endif\n\n#else\n\n vec3 fdx = dFdx( vViewPosition );\n vec3 fdy = dFdy( vViewPosition );\n vec3 normal = normalize( cross( fdx, fdy ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n normal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n",THREE.ShaderChunk.normalmap_pars_fragment="#ifdef USE_NORMALMAP\n\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n\n\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n vec3 q0 = dFdx( eye_pos.xyz );\n vec3 q1 = dFdy( eye_pos.xyz );\n vec2 st0 = dFdx( vUv.st );\n vec2 st1 = dFdy( vUv.st );\n\n vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n vec3 N = normalize( surf_norm );\n\n vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n mapN.xy = normalScale * mapN.xy;\n mat3 tsn = mat3( S, T, N );\n return normalize( tsn * mapN );\n\n }\n\n#endif\n",THREE.ShaderChunk.project_vertex="#ifdef USE_SKINNING\n\n vec4 mvPosition = modelViewMatrix * skinned;\n\n#else\n\n vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;\n",THREE.ShaderChunk.shadowmap_fragment="#ifdef USE_SHADOWMAP\n\n for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n float texelSizeY = 1.0 / shadowMapSize[ i ].y;\n\n float shadow = 0.0;\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n bool isPointLight = shadowDarkness[ i ] < 0.0;\n\n if ( isPointLight ) {\n\n float realShadowDarkness = abs( shadowDarkness[ i ] );\n\n vec3 lightToPosition = vShadowCoord[ i ].xyz;\n\n #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n vec3 bd3D = normalize( lightToPosition );\n float dp = length( lightToPosition );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n\n #if defined( SHADOWMAP_TYPE_PCF )\n const float Dr = 1.25;\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n const float Dr = 2.25;\n #endif\n\n float os = Dr * 2.0 * texelSizeY;\n\n const vec3 Gsd = vec3( - 1, 0, 1 );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n shadow *= realShadowDarkness * ( 1.0 / 21.0 );\n\n #else \n vec3 bd3D = normalize( lightToPosition );\n float dp = length( lightToPosition );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n shadow *= realShadowDarkness;\n\n #endif\n\n } else {\n\n#endif \n float texelSizeX = 1.0 / shadowMapSize[ i ].x;\n\n vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n\n bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n bool inFrustum = all( inFrustumVec );\n\n bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n bool frustumTest = all( frustumTestVec );\n\n if ( frustumTest ) {\n\n #if defined( SHADOWMAP_TYPE_PCF )\n\n\n /*\n for ( float y = -1.25; y <= 1.25; y += 1.25 )\n for ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n if ( fDepth < shadowCoord.z )\n shadow += 1.0;\n }\n shadow /= 9.0;\n */\n\n shadowCoord.z += shadowBias[ i ];\n\n const float ShadowDelta = 1.0 / 9.0;\n\n float xPixelOffset = texelSizeX;\n float yPixelOffset = texelSizeY;\n\n float dx0 = - 1.25 * xPixelOffset;\n float dy0 = - 1.25 * yPixelOffset;\n float dx1 = 1.25 * xPixelOffset;\n float dy1 = 1.25 * yPixelOffset;\n\n float fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n shadow *= shadowDarkness[ i ];\n\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n\n shadowCoord.z += shadowBias[ i ];\n\n float xPixelOffset = texelSizeX;\n float yPixelOffset = texelSizeY;\n\n float dx0 = - 1.0 * xPixelOffset;\n float dy0 = - 1.0 * yPixelOffset;\n float dx1 = 1.0 * xPixelOffset;\n float dy1 = 1.0 * yPixelOffset;\n\n mat3 shadowKernel;\n mat3 depthKernel;\n\n depthKernel[ 0 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n depthKernel[ 0 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n depthKernel[ 0 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n depthKernel[ 1 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n depthKernel[ 1 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n depthKernel[ 1 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n depthKernel[ 2 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n depthKernel[ 2 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n depthKernel[ 2 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n vec3 shadowZ = vec3( shadowCoord.z );\n shadowKernel[ 0 ] = vec3( lessThan( depthKernel[ 0 ], shadowZ ) );\n shadowKernel[ 0 ] *= vec3( 0.25 );\n\n shadowKernel[ 1 ] = vec3( lessThan( depthKernel[ 1 ], shadowZ ) );\n shadowKernel[ 1 ] *= vec3( 0.25 );\n\n shadowKernel[ 2 ] = vec3( lessThan( depthKernel[ 2 ], shadowZ ) );\n shadowKernel[ 2 ] *= vec3( 0.25 );\n\n vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[ i ].xy );\n\n shadowKernel[ 0 ] = mix( shadowKernel[ 1 ], shadowKernel[ 0 ], fractionalCoord.x );\n shadowKernel[ 1 ] = mix( shadowKernel[ 2 ], shadowKernel[ 1 ], fractionalCoord.x );\n\n vec4 shadowValues;\n shadowValues.x = mix( shadowKernel[ 0 ][ 1 ], shadowKernel[ 0 ][ 0 ], fractionalCoord.y );\n shadowValues.y = mix( shadowKernel[ 0 ][ 2 ], shadowKernel[ 0 ][ 1 ], fractionalCoord.y );\n shadowValues.z = mix( shadowKernel[ 1 ][ 1 ], shadowKernel[ 1 ][ 0 ], fractionalCoord.y );\n shadowValues.w = mix( shadowKernel[ 1 ][ 2 ], shadowKernel[ 1 ][ 1 ], fractionalCoord.y );\n\n shadow = dot( shadowValues, vec4( 1.0 ) ) * shadowDarkness[ i ];\n\n #else \n shadowCoord.z += shadowBias[ i ];\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n shadow = shadowDarkness[ i ];\n\n #endif\n\n }\n\n#ifdef SHADOWMAP_DEBUG\n\n if ( inFrustum ) {\n\n if ( i == 0 ) {\n\n outgoingLight *= vec3( 1.0, 0.5, 0.0 );\n\n } else if ( i == 1 ) {\n\n outgoingLight *= vec3( 0.0, 1.0, 0.8 );\n\n } else {\n\n outgoingLight *= vec3( 0.0, 0.5, 1.0 );\n\n }\n\n }\n\n#endif\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n }\n\n#endif\n\n shadowMask = shadowMask * vec3( 1.0 - shadow );\n\n }\n\n#endif\n",THREE.ShaderChunk.shadowmap_pars_fragment="#ifdef USE_SHADOWMAP\n\n uniform sampler2D shadowMap[ MAX_SHADOWS ];\n uniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform float shadowBias[ MAX_SHADOWS ];\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n float unpackDepth( const in vec4 rgba_depth ) {\n\n const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n float depth = dot( rgba_depth, bit_shift );\n return depth;\n\n }\n\n #if defined(POINT_LIGHT_SHADOWS)\n\n\n void adjustShadowValue1K( const float testDepth, const vec4 textureData, const float bias, inout float shadowValue ) {\n\n const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n if ( testDepth >= dot( textureData, bitSh ) * 1000.0 + bias )\n shadowValue += 1.0;\n\n }\n\n\n vec2 cubeToUV( vec3 v, float texelSizeY ) {\n\n\n vec3 absV = abs( v );\n\n\n float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n absV *= scaleToCube;\n\n\n v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\n\n\n vec2 planar = v.xy;\n\n float almostATexel = 1.5 * texelSizeY;\n float almostOne = 1.0 - almostATexel;\n\n if ( absV.z >= almostOne ) {\n\n if ( v.z > 0.0 )\n planar.x = 4.0 - v.x;\n\n } else if ( absV.x >= almostOne ) {\n\n float signX = sign( v.x );\n planar.x = v.z * signX + 2.0 * signX;\n\n } else if ( absV.y >= almostOne ) {\n\n float signY = sign( v.y );\n planar.x = v.x + 2.0 * signY + 2.0;\n planar.y = v.z * signY - 2.0;\n\n }\n\n\n return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\n }\n\n #endif\n\n#endif\n",THREE.ShaderChunk.shadowmap_pars_vertex="#ifdef USE_SHADOWMAP\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n#endif",THREE.ShaderChunk.shadowmap_vertex="#ifdef USE_SHADOWMAP\n\n for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n }\n\n#endif",THREE.ShaderChunk.skinbase_vertex="#ifdef USE_SKINNING\n\n mat4 boneMatX = getBoneMatrix( skinIndex.x );\n mat4 boneMatY = getBoneMatrix( skinIndex.y );\n mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n mat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif",THREE.ShaderChunk.skinning_pars_vertex="#ifdef USE_SKINNING\n\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n\n #ifdef BONE_TEXTURE\n\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n\n mat4 getBoneMatrix( const in float i ) {\n\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n\n y = dy * ( y + 0.5 );\n\n vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n mat4 bone = mat4( v1, v2, v3, v4 );\n\n return bone;\n\n }\n\n #else\n\n uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n mat4 getBoneMatrix( const in float i ) {\n\n mat4 bone = boneGlobalMatrices[ int(i) ];\n return bone;\n\n }\n\n #endif\n\n#endif\n",THREE.ShaderChunk.skinning_vertex="#ifdef USE_SKINNING\n\n vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\n vec4 skinned = vec4( 0.0 );\n skinned += boneMatX * skinVertex * skinWeight.x;\n skinned += boneMatY * skinVertex * skinWeight.y;\n skinned += boneMatZ * skinVertex * skinWeight.z;\n skinned += boneMatW * skinVertex * skinWeight.w;\n skinned = bindMatrixInverse * skinned;\n\n#endif\n",THREE.ShaderChunk.skinnormal_vertex="#ifdef USE_SKINNING\n\n mat4 skinMatrix = mat4( 0.0 );\n skinMatrix += skinWeight.x * boneMatX;\n skinMatrix += skinWeight.y * boneMatY;\n skinMatrix += skinWeight.z * boneMatZ;\n skinMatrix += skinWeight.w * boneMatW;\n skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\n objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\n#endif\n",THREE.ShaderChunk.specularmap_fragment="float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n\n#else\n\n specularStrength = 1.0;\n\n#endif",THREE.ShaderChunk.specularmap_pars_fragment="#ifdef USE_SPECULARMAP\n\n uniform sampler2D specularMap;\n\n#endif",THREE.ShaderChunk.uv2_pars_fragment="#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n varying vec2 vUv2;\n\n#endif",THREE.ShaderChunk.uv2_pars_vertex="#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n attribute vec2 uv2;\n varying vec2 vUv2;\n\n#endif",THREE.ShaderChunk.uv2_vertex="#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n vUv2 = uv2;\n\n#endif",THREE.ShaderChunk.uv_pars_fragment="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n\n#endif",THREE.ShaderChunk.uv_pars_vertex="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n\n#endif\n",THREE.ShaderChunk.uv_vertex="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif",THREE.ShaderChunk.worldpos_vertex="#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n #ifdef USE_SKINNING\n\n vec4 worldPosition = modelMatrix * skinned;\n\n #else\n\n vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n\n #endif\n\n#endif\n",THREE.UniformsUtils={merge:function(uniforms){for(var merged={},u=0;u dashSize ) {"," discard;"," }"," vec3 outgoingLight = vec3( 0.0 );"," vec4 diffuseColor = vec4( diffuse, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.color_fragment," outgoingLight = diffuseColor.rgb;",THREE.ShaderChunk.fog_fragment," gl_FragColor = vec4( outgoingLight, diffuseColor.a );","}"].join("\n")},depth:{uniforms:{mNear:{type:"f",value:1},mFar:{type:"f",value:2e3},opacity:{type:"f",value:1}},vertexShader:[THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.begin_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.project_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform float mNear;","uniform float mFar;","uniform float opacity;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {",THREE.ShaderChunk.logdepthbuf_fragment," #ifdef USE_LOGDEPTHBUF_EXT"," float depth = gl_FragDepthEXT / gl_FragCoord.w;"," #else"," float depth = gl_FragCoord.z / gl_FragCoord.w;"," #endif"," float color = 1.0 - smoothstep( mNear, mFar, depth );"," gl_FragColor = vec4( vec3( color ), opacity );","}"].join("\n")},normal:{uniforms:{opacity:{type:"f",value:1}},vertexShader:["varying vec3 vNormal;",THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {"," vNormal = normalize( normalMatrix * normal );",THREE.ShaderChunk.begin_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.project_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform float opacity;","varying vec3 vNormal;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {"," gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},cube:{uniforms:{tCube:{type:"t",value:null},tFlip:{type:"f",value:-1}},vertexShader:["varying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {"," vWorldPosition = transformDirection( position, modelMatrix );"," gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform samplerCube tCube;","uniform float tFlip;","varying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {"," gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},equirect:{uniforms:{tEquirect:{type:"t",value:null},tFlip:{type:"f",value:-1}},vertexShader:["varying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {"," vWorldPosition = transformDirection( position, modelMatrix );"," gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform sampler2D tEquirect;","uniform float tFlip;","varying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {","vec3 direction = normalize( vWorldPosition );","vec2 sampleUV;","sampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );","sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;","gl_FragColor = texture2D( tEquirect, sampleUV );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},depthRGBA:{uniforms:{},vertexShader:[THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.begin_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.project_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:[THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"vec4 pack_depth( const in float depth ) {"," const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );"," const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );"," vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );"," res -= res.xxyz * bit_mask;"," return res;","}","void main() {",THREE.ShaderChunk.logdepthbuf_fragment," #ifdef USE_LOGDEPTHBUF_EXT"," gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );"," #else"," gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );"," #endif","}"].join("\n")},distanceRGBA:{uniforms:{lightPos:{type:"v3",value:new THREE.Vector3(0,0,0)}},vertexShader:["varying vec4 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.begin_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.project_vertex,THREE.ShaderChunk.worldpos_vertex,"vWorldPosition = worldPosition;","}"].join("\n"),fragmentShader:["uniform vec3 lightPos;","varying vec4 vWorldPosition;",THREE.ShaderChunk.common,"vec4 pack1K ( float depth ) {"," depth /= 1000.0;"," const vec4 bitSh = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );"," const vec4 bitMsk = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );"," vec4 res = fract( depth * bitSh );"," res -= res.xxyz * bitMsk;"," return res; ","}","float unpack1K ( vec4 color ) {"," const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );"," return dot( color, bitSh ) * 1000.0;","}","void main () {"," gl_FragColor = pack1K( length( vWorldPosition.xyz - lightPos.xyz ) );","}"].join("\n")}},THREE.WebGLRenderer=function(parameters){function glClearColor(r,g,b,a){_premultipliedAlpha===!0&&(r*=a,g*=a,b*=a),_gl.clearColor(r,g,b,a)}function setDefaultGLState(){state.init(),_gl.viewport(_viewportX,_viewportY,_viewportWidth,_viewportHeight),glClearColor(_clearColor.r,_clearColor.g,_clearColor.b,_clearAlpha)}function resetGLState(){_currentProgram=null,_currentCamera=null,_currentGeometryProgram="",_currentMaterialId=-1,_lightsNeedUpdate=!0,state.reset()}function onContextLost(event){event.preventDefault(),resetGLState(),setDefaultGLState(),properties.clear()}function onTextureDispose(event){var texture=event.target;texture.removeEventListener("dispose",onTextureDispose),deallocateTexture(texture),_infoMemory.textures--}function onRenderTargetDispose(event){var renderTarget=event.target;renderTarget.removeEventListener("dispose",onRenderTargetDispose),deallocateRenderTarget(renderTarget),_infoMemory.textures--}function onMaterialDispose(event){var material=event.target;material.removeEventListener("dispose",onMaterialDispose),deallocateMaterial(material)}function deallocateTexture(texture){var textureProperties=properties.get(texture);if(texture.image&&textureProperties.__image__webglTextureCube)_gl.deleteTexture(textureProperties.__image__webglTextureCube);else{if(void 0===textureProperties.__webglInit)return;_gl.deleteTexture(textureProperties.__webglTexture)}properties["delete"](texture)}function deallocateRenderTarget(renderTarget){var renderTargetProperties=properties.get(renderTarget),textureProperties=properties.get(renderTarget.texture);if(renderTarget&&void 0!==textureProperties.__webglTexture){if(_gl.deleteTexture(textureProperties.__webglTexture),renderTarget instanceof THREE.WebGLRenderTargetCube)for(var i=0;6>i;i++)_gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]),_gl.deleteRenderbuffer(renderTargetProperties.__webglRenderbuffer[i]);else _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer),_gl.deleteRenderbuffer(renderTargetProperties.__webglRenderbuffer);properties["delete"](renderTarget.texture),properties["delete"](renderTarget)}}function deallocateMaterial(material){releaseMaterialProgramReference(material),properties["delete"](material)}function releaseMaterialProgramReference(material){var programInfo=properties.get(material).program;material.program=void 0,void 0!==programInfo&&programCache.releaseProgram(programInfo)}function setupVertexAttributes(material,program,geometry,startIndex){var extension;if(geometry instanceof THREE.InstancedBufferGeometry&&(extension=extensions.get("ANGLE_instanced_arrays"),null===extension))return void console.error("THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.");void 0===startIndex&&(startIndex=0),state.initAttributes();var geometryAttributes=geometry.attributes,programAttributes=program.getAttributes(),materialDefaultAttributeValues=material.defaultAttributeValues;for(var name in programAttributes){var programAttribute=programAttributes[name];if(programAttribute>=0){var geometryAttribute=geometryAttributes[name];if(void 0!==geometryAttribute){var size=geometryAttribute.itemSize,buffer=objects.getAttributeBuffer(geometryAttribute);if(geometryAttribute instanceof THREE.InterleavedBufferAttribute){var data=geometryAttribute.data,stride=data.stride,offset=geometryAttribute.offset;data instanceof THREE.InstancedInterleavedBuffer?(state.enableAttributeAndDivisor(programAttribute,data.meshPerAttribute,extension),void 0===geometry.maxInstancedCount&&(geometry.maxInstancedCount=data.meshPerAttribute*data.count)):state.enableAttribute(programAttribute),_gl.bindBuffer(_gl.ARRAY_BUFFER,buffer),_gl.vertexAttribPointer(programAttribute,size,_gl.FLOAT,!1,stride*data.array.BYTES_PER_ELEMENT,(startIndex*stride+offset)*data.array.BYTES_PER_ELEMENT)}else geometryAttribute instanceof THREE.InstancedBufferAttribute?(state.enableAttributeAndDivisor(programAttribute,geometryAttribute.meshPerAttribute,extension),void 0===geometry.maxInstancedCount&&(geometry.maxInstancedCount=geometryAttribute.meshPerAttribute*geometryAttribute.count)):state.enableAttribute(programAttribute),_gl.bindBuffer(_gl.ARRAY_BUFFER,buffer),_gl.vertexAttribPointer(programAttribute,size,_gl.FLOAT,!1,0,startIndex*size*4)}else if(void 0!==materialDefaultAttributeValues){var value=materialDefaultAttributeValues[name];if(void 0!==value)switch(value.length){case 2:_gl.vertexAttrib2fv(programAttribute,value);break;case 3:_gl.vertexAttrib3fv(programAttribute,value);break;case 4:_gl.vertexAttrib4fv(programAttribute,value);break;default:_gl.vertexAttrib1fv(programAttribute,value)}}}}state.disableUnusedAttributes()}function numericalSort(a,b){return b[0]-a[0]}function painterSortStable(a,b){return a.object.renderOrder!==b.object.renderOrder?a.object.renderOrder-b.object.renderOrder:a.material.id!==b.material.id?a.material.id-b.material.id:a.z!==b.z?a.z-b.z:a.id-b.id}function reversePainterSortStable(a,b){return a.object.renderOrder!==b.object.renderOrder?a.object.renderOrder-b.object.renderOrder:a.z!==b.z?b.z-a.z:a.id-b.id}function pushRenderItem(object,geometry,material,z,group){var array,index;material.transparent?(array=transparentObjects,index=++transparentObjectsLastIndex):(array=opaqueObjects,index=++opaqueObjectsLastIndex);var renderItem=array[index];void 0!==renderItem?(renderItem.id=object.id,renderItem.object=object,renderItem.geometry=geometry,renderItem.material=material,renderItem.z=_vector3.z,renderItem.group=group):(renderItem={id:object.id,object:object,geometry:geometry,material:material,z:_vector3.z,group:group},array.push(renderItem))}function projectObject(object,camera){if(object.visible!==!1){if(0!==(object.channels.mask&camera.channels.mask))if(object instanceof THREE.Light)lights.push(object);else if(object instanceof THREE.Sprite)sprites.push(object);else if(object instanceof THREE.LensFlare)lensFlares.push(object);else if(object instanceof THREE.ImmediateRenderObject)_this.sortObjects===!0&&(_vector3.setFromMatrixPosition(object.matrixWorld),_vector3.applyProjection(_projScreenMatrix)),pushRenderItem(object,null,object.material,_vector3.z,null);else if((object instanceof THREE.Mesh||object instanceof THREE.Line||object instanceof THREE.Points)&&(object instanceof THREE.SkinnedMesh&&object.skeleton.update(),object.frustumCulled===!1||_frustum.intersectsObject(object)===!0)){var material=object.material;if(material.visible===!0){_this.sortObjects===!0&&(_vector3.setFromMatrixPosition(object.matrixWorld),_vector3.applyProjection(_projScreenMatrix));var geometry=objects.update(object);if(material instanceof THREE.MeshFaceMaterial)for(var groups=geometry.groups,materials=material.materials,i=0,l=groups.length;l>i;i++){var group=groups[i],groupMaterial=materials[group.materialIndex];groupMaterial.visible===!0&&pushRenderItem(object,geometry,groupMaterial,_vector3.z,group)}else pushRenderItem(object,geometry,material,_vector3.z,null)}}for(var children=object.children,i=0,l=children.length;l>i;i++)projectObject(children[i],camera)}}function renderObjects(renderList,camera,lights,fog,overrideMaterial){for(var i=0,l=renderList.length;l>i;i++){var renderItem=renderList[i],object=renderItem.object,geometry=renderItem.geometry,material=void 0===overrideMaterial?renderItem.material:overrideMaterial,group=renderItem.group;if(object.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse,object.matrixWorld),object.normalMatrix.getNormalMatrix(object.modelViewMatrix),object instanceof THREE.ImmediateRenderObject){setMaterial(material);var program=setProgram(camera,lights,fog,material,object);_currentGeometryProgram="",object.render(function(object){_this.renderBufferImmediate(object,program,material)})}else _this.renderBufferDirect(camera,lights,fog,geometry,material,object,group)}}function initMaterial(material,lights,fog,object){var materialProperties=properties.get(material),parameters=programCache.getParameters(material,lights,fog,object),code=programCache.getProgramCode(material,parameters),program=materialProperties.program,programChange=!0;if(void 0===program)material.addEventListener("dispose",onMaterialDispose);else if(program.code!==code)releaseMaterialProgramReference(material);else{if(void 0!==parameters.shaderID)return;programChange=!1}if(programChange){if(parameters.shaderID){var shader=THREE.ShaderLib[parameters.shaderID];materialProperties.__webglShader={name:material.type,uniforms:THREE.UniformsUtils.clone(shader.uniforms),vertexShader:shader.vertexShader,fragmentShader:shader.fragmentShader}}else materialProperties.__webglShader={name:material.type,uniforms:material.uniforms,vertexShader:material.vertexShader,fragmentShader:material.fragmentShader};material.__webglShader=materialProperties.__webglShader,program=programCache.acquireProgram(material,parameters,code),materialProperties.program=program,material.program=program}var attributes=program.getAttributes();if(material.morphTargets){material.numSupportedMorphTargets=0;for(var i=0;i<_this.maxMorphTargets;i++)attributes["morphTarget"+i]>=0&&material.numSupportedMorphTargets++}if(material.morphNormals)for(material.numSupportedMorphNormals=0,i=0;i<_this.maxMorphNormals;i++)attributes["morphNormal"+i]>=0&&material.numSupportedMorphNormals++;materialProperties.uniformsList=[];var uniformLocations=materialProperties.program.getUniforms();for(var u in materialProperties.__webglShader.uniforms){var location=uniformLocations[u];location&&materialProperties.uniformsList.push([materialProperties.__webglShader.uniforms[u],location])}}function setMaterial(material){setMaterialFaces(material),material.transparent===!0?state.setBlending(material.blending,material.blendEquation,material.blendSrc,material.blendDst,material.blendEquationAlpha,material.blendSrcAlpha,material.blendDstAlpha):state.setBlending(THREE.NoBlending),state.setDepthFunc(material.depthFunc),state.setDepthTest(material.depthTest),state.setDepthWrite(material.depthWrite),state.setColorWrite(material.colorWrite),state.setPolygonOffset(material.polygonOffset,material.polygonOffsetFactor,material.polygonOffsetUnits)}function setMaterialFaces(material){material.side!==THREE.DoubleSide?state.enable(_gl.CULL_FACE):state.disable(_gl.CULL_FACE),state.setFlipSided(material.side===THREE.BackSide)}function setProgram(camera,lights,fog,material,object){_usedTextureUnits=0;var materialProperties=properties.get(material);!material.needsUpdate&&materialProperties.program||(initMaterial(material,lights,fog,object),material.needsUpdate=!1);var refreshProgram=!1,refreshMaterial=!1,refreshLights=!1,program=materialProperties.program,p_uniforms=program.getUniforms(),m_uniforms=materialProperties.__webglShader.uniforms;if(program.id!==_currentProgram&&(_gl.useProgram(program.program),_currentProgram=program.id,refreshProgram=!0,refreshMaterial=!0,refreshLights=!0),material.id!==_currentMaterialId&&(-1===_currentMaterialId&&(refreshLights=!0),_currentMaterialId=material.id,refreshMaterial=!0),(refreshProgram||camera!==_currentCamera)&&(_gl.uniformMatrix4fv(p_uniforms.projectionMatrix,!1,camera.projectionMatrix.elements),capabilities.logarithmicDepthBuffer&&_gl.uniform1f(p_uniforms.logDepthBufFC,2/(Math.log(camera.far+1)/Math.LN2)),camera!==_currentCamera&&(_currentCamera=camera),(material instanceof THREE.ShaderMaterial||material instanceof THREE.MeshPhongMaterial||material.envMap)&&void 0!==p_uniforms.cameraPosition&&(_vector3.setFromMatrixPosition(camera.matrixWorld),_gl.uniform3f(p_uniforms.cameraPosition,_vector3.x,_vector3.y,_vector3.z)),(material instanceof THREE.MeshPhongMaterial||material instanceof THREE.MeshLambertMaterial||material instanceof THREE.MeshBasicMaterial||material instanceof THREE.ShaderMaterial||material.skinning)&&void 0!==p_uniforms.viewMatrix&&_gl.uniformMatrix4fv(p_uniforms.viewMatrix,!1,camera.matrixWorldInverse.elements)),material.skinning)if(object.bindMatrix&&void 0!==p_uniforms.bindMatrix&&_gl.uniformMatrix4fv(p_uniforms.bindMatrix,!1,object.bindMatrix.elements),object.bindMatrixInverse&&void 0!==p_uniforms.bindMatrixInverse&&_gl.uniformMatrix4fv(p_uniforms.bindMatrixInverse,!1,object.bindMatrixInverse.elements),capabilities.floatVertexTextures&&object.skeleton&&object.skeleton.useVertexTexture){if(void 0!==p_uniforms.boneTexture){var textureUnit=getTextureUnit();_gl.uniform1i(p_uniforms.boneTexture,textureUnit),_this.setTexture(object.skeleton.boneTexture,textureUnit)}void 0!==p_uniforms.boneTextureWidth&&_gl.uniform1i(p_uniforms.boneTextureWidth,object.skeleton.boneTextureWidth),void 0!==p_uniforms.boneTextureHeight&&_gl.uniform1i(p_uniforms.boneTextureHeight,object.skeleton.boneTextureHeight)}else object.skeleton&&object.skeleton.boneMatrices&&void 0!==p_uniforms.boneGlobalMatrices&&_gl.uniformMatrix4fv(p_uniforms.boneGlobalMatrices,!1,object.skeleton.boneMatrices);return refreshMaterial&&(fog&&material.fog&&refreshUniformsFog(m_uniforms,fog),(material instanceof THREE.MeshPhongMaterial||material instanceof THREE.MeshLambertMaterial||material.lights)&&(_lightsNeedUpdate&&(refreshLights=!0,setupLights(lights,camera),_lightsNeedUpdate=!1),refreshLights?(refreshUniformsLights(m_uniforms,_lights),markUniformsLightsNeedsUpdate(m_uniforms,!0)):markUniformsLightsNeedsUpdate(m_uniforms,!1)),(material instanceof THREE.MeshBasicMaterial||material instanceof THREE.MeshLambertMaterial||material instanceof THREE.MeshPhongMaterial)&&refreshUniformsCommon(m_uniforms,material),material instanceof THREE.LineBasicMaterial?refreshUniformsLine(m_uniforms,material):material instanceof THREE.LineDashedMaterial?(refreshUniformsLine(m_uniforms,material),refreshUniformsDash(m_uniforms,material)):material instanceof THREE.PointsMaterial?refreshUniformsParticle(m_uniforms,material):material instanceof THREE.MeshPhongMaterial?refreshUniformsPhong(m_uniforms,material):material instanceof THREE.MeshDepthMaterial?(m_uniforms.mNear.value=camera.near,m_uniforms.mFar.value=camera.far,m_uniforms.opacity.value=material.opacity):material instanceof THREE.MeshNormalMaterial&&(m_uniforms.opacity.value=material.opacity),object.receiveShadow&&!material._shadowPass&&refreshUniformsShadow(m_uniforms,lights,camera),loadUniformsGeneric(materialProperties.uniformsList)),loadUniformsMatrices(p_uniforms,object),void 0!==p_uniforms.modelMatrix&&_gl.uniformMatrix4fv(p_uniforms.modelMatrix,!1,object.matrixWorld.elements),program}function refreshUniformsCommon(uniforms,material){uniforms.opacity.value=material.opacity,uniforms.diffuse.value=material.color,material.emissive&&(uniforms.emissive.value=material.emissive),uniforms.map.value=material.map,uniforms.specularMap.value=material.specularMap,uniforms.alphaMap.value=material.alphaMap,material.aoMap&&(uniforms.aoMap.value=material.aoMap,uniforms.aoMapIntensity.value=material.aoMapIntensity);var uvScaleMap;if(material.map?uvScaleMap=material.map:material.specularMap?uvScaleMap=material.specularMap:material.displacementMap?uvScaleMap=material.displacementMap:material.normalMap?uvScaleMap=material.normalMap:material.bumpMap?uvScaleMap=material.bumpMap:material.alphaMap?uvScaleMap=material.alphaMap:material.emissiveMap&&(uvScaleMap=material.emissiveMap),void 0!==uvScaleMap){uvScaleMap instanceof THREE.WebGLRenderTarget&&(uvScaleMap=uvScaleMap.texture);var offset=uvScaleMap.offset,repeat=uvScaleMap.repeat;uniforms.offsetRepeat.value.set(offset.x,offset.y,repeat.x,repeat.y)}uniforms.envMap.value=material.envMap,uniforms.flipEnvMap.value=material.envMap instanceof THREE.WebGLRenderTargetCube?1:-1,uniforms.reflectivity.value=material.reflectivity,uniforms.refractionRatio.value=material.refractionRatio}function refreshUniformsLine(uniforms,material){uniforms.diffuse.value=material.color,uniforms.opacity.value=material.opacity;
+}function refreshUniformsDash(uniforms,material){uniforms.dashSize.value=material.dashSize,uniforms.totalSize.value=material.dashSize+material.gapSize,uniforms.scale.value=material.scale}function refreshUniformsParticle(uniforms,material){if(uniforms.psColor.value=material.color,uniforms.opacity.value=material.opacity,uniforms.size.value=material.size,uniforms.scale.value=_canvas.height/2,uniforms.map.value=material.map,null!==material.map){var offset=material.map.offset,repeat=material.map.repeat;uniforms.offsetRepeat.value.set(offset.x,offset.y,repeat.x,repeat.y)}}function refreshUniformsFog(uniforms,fog){uniforms.fogColor.value=fog.color,fog instanceof THREE.Fog?(uniforms.fogNear.value=fog.near,uniforms.fogFar.value=fog.far):fog instanceof THREE.FogExp2&&(uniforms.fogDensity.value=fog.density)}function refreshUniformsPhong(uniforms,material){uniforms.specular.value=material.specular,uniforms.shininess.value=Math.max(material.shininess,1e-4),material.lightMap&&(uniforms.lightMap.value=material.lightMap,uniforms.lightMapIntensity.value=material.lightMapIntensity),material.emissiveMap&&(uniforms.emissiveMap.value=material.emissiveMap),material.bumpMap&&(uniforms.bumpMap.value=material.bumpMap,uniforms.bumpScale.value=material.bumpScale),material.normalMap&&(uniforms.normalMap.value=material.normalMap,uniforms.normalScale.value.copy(material.normalScale)),material.displacementMap&&(uniforms.displacementMap.value=material.displacementMap,uniforms.displacementScale.value=material.displacementScale,uniforms.displacementBias.value=material.displacementBias)}function refreshUniformsLights(uniforms,lights){uniforms.ambientLightColor.value=lights.ambient,uniforms.directionalLightColor.value=lights.directional.colors,uniforms.directionalLightDirection.value=lights.directional.positions,uniforms.pointLightColor.value=lights.point.colors,uniforms.pointLightPosition.value=lights.point.positions,uniforms.pointLightDistance.value=lights.point.distances,uniforms.pointLightDecay.value=lights.point.decays,uniforms.spotLightColor.value=lights.spot.colors,uniforms.spotLightPosition.value=lights.spot.positions,uniforms.spotLightDistance.value=lights.spot.distances,uniforms.spotLightDirection.value=lights.spot.directions,uniforms.spotLightAngleCos.value=lights.spot.anglesCos,uniforms.spotLightExponent.value=lights.spot.exponents,uniforms.spotLightDecay.value=lights.spot.decays,uniforms.hemisphereLightSkyColor.value=lights.hemi.skyColors,uniforms.hemisphereLightGroundColor.value=lights.hemi.groundColors,uniforms.hemisphereLightDirection.value=lights.hemi.positions}function markUniformsLightsNeedsUpdate(uniforms,value){uniforms.ambientLightColor.needsUpdate=value,uniforms.directionalLightColor.needsUpdate=value,uniforms.directionalLightDirection.needsUpdate=value,uniforms.pointLightColor.needsUpdate=value,uniforms.pointLightPosition.needsUpdate=value,uniforms.pointLightDistance.needsUpdate=value,uniforms.pointLightDecay.needsUpdate=value,uniforms.spotLightColor.needsUpdate=value,uniforms.spotLightPosition.needsUpdate=value,uniforms.spotLightDistance.needsUpdate=value,uniforms.spotLightDirection.needsUpdate=value,uniforms.spotLightAngleCos.needsUpdate=value,uniforms.spotLightExponent.needsUpdate=value,uniforms.spotLightDecay.needsUpdate=value,uniforms.hemisphereLightSkyColor.needsUpdate=value,uniforms.hemisphereLightGroundColor.needsUpdate=value,uniforms.hemisphereLightDirection.needsUpdate=value}function refreshUniformsShadow(uniforms,lights,camera){if(uniforms.shadowMatrix)for(var j=0,i=0,il=lights.length;il>i;i++){var light=lights[i];if(light.castShadow===!0&&(light instanceof THREE.PointLight||light instanceof THREE.SpotLight||light instanceof THREE.DirectionalLight)){var shadow=light.shadow;light instanceof THREE.PointLight?(_vector3.setFromMatrixPosition(light.matrixWorld).negate(),shadow.matrix.identity().setPosition(_vector3),uniforms.shadowDarkness.value[j]=-shadow.darkness):uniforms.shadowDarkness.value[j]=shadow.darkness,uniforms.shadowMatrix.value[j]=shadow.matrix,uniforms.shadowMap.value[j]=shadow.map,uniforms.shadowMapSize.value[j]=shadow.mapSize,uniforms.shadowBias.value[j]=shadow.bias,j++}}}function loadUniformsMatrices(uniforms,object){_gl.uniformMatrix4fv(uniforms.modelViewMatrix,!1,object.modelViewMatrix.elements),uniforms.normalMatrix&&_gl.uniformMatrix3fv(uniforms.normalMatrix,!1,object.normalMatrix.elements)}function getTextureUnit(){var textureUnit=_usedTextureUnits;return textureUnit>=capabilities.maxTextures&&console.warn("WebGLRenderer: trying to use "+textureUnit+" texture units while this GPU supports only "+capabilities.maxTextures),_usedTextureUnits+=1,textureUnit}function loadUniformsGeneric(uniforms){for(var texture,textureUnit,j=0,jl=uniforms.length;jl>j;j++){var uniform=uniforms[j][0];if(uniform.needsUpdate!==!1){var type=uniform.type,value=uniform.value,location=uniforms[j][1];switch(type){case"1i":_gl.uniform1i(location,value);break;case"1f":_gl.uniform1f(location,value);break;case"2f":_gl.uniform2f(location,value[0],value[1]);break;case"3f":_gl.uniform3f(location,value[0],value[1],value[2]);break;case"4f":_gl.uniform4f(location,value[0],value[1],value[2],value[3]);break;case"1iv":_gl.uniform1iv(location,value);break;case"3iv":_gl.uniform3iv(location,value);break;case"1fv":_gl.uniform1fv(location,value);break;case"2fv":_gl.uniform2fv(location,value);break;case"3fv":_gl.uniform3fv(location,value);break;case"4fv":_gl.uniform4fv(location,value);break;case"Matrix3fv":_gl.uniformMatrix3fv(location,!1,value);break;case"Matrix4fv":_gl.uniformMatrix4fv(location,!1,value);break;case"i":_gl.uniform1i(location,value);break;case"f":_gl.uniform1f(location,value);break;case"v2":_gl.uniform2f(location,value.x,value.y);break;case"v3":_gl.uniform3f(location,value.x,value.y,value.z);break;case"v4":_gl.uniform4f(location,value.x,value.y,value.z,value.w);break;case"c":_gl.uniform3f(location,value.r,value.g,value.b);break;case"iv1":_gl.uniform1iv(location,value);break;case"iv":_gl.uniform3iv(location,value);break;case"fv1":_gl.uniform1fv(location,value);break;case"fv":_gl.uniform3fv(location,value);break;case"v2v":void 0===uniform._array&&(uniform._array=new Float32Array(2*value.length));for(var i=0,i2=0,il=value.length;il>i;i++,i2+=2)uniform._array[i2+0]=value[i].x,uniform._array[i2+1]=value[i].y;_gl.uniform2fv(location,uniform._array);break;case"v3v":void 0===uniform._array&&(uniform._array=new Float32Array(3*value.length));for(var i=0,i3=0,il=value.length;il>i;i++,i3+=3)uniform._array[i3+0]=value[i].x,uniform._array[i3+1]=value[i].y,uniform._array[i3+2]=value[i].z;_gl.uniform3fv(location,uniform._array);break;case"v4v":void 0===uniform._array&&(uniform._array=new Float32Array(4*value.length));for(var i=0,i4=0,il=value.length;il>i;i++,i4+=4)uniform._array[i4+0]=value[i].x,uniform._array[i4+1]=value[i].y,uniform._array[i4+2]=value[i].z,uniform._array[i4+3]=value[i].w;_gl.uniform4fv(location,uniform._array);break;case"m3":_gl.uniformMatrix3fv(location,!1,value.elements);break;case"m3v":void 0===uniform._array&&(uniform._array=new Float32Array(9*value.length));for(var i=0,il=value.length;il>i;i++)value[i].flattenToArrayOffset(uniform._array,9*i);_gl.uniformMatrix3fv(location,!1,uniform._array);break;case"m4":_gl.uniformMatrix4fv(location,!1,value.elements);break;case"m4v":void 0===uniform._array&&(uniform._array=new Float32Array(16*value.length));for(var i=0,il=value.length;il>i;i++)value[i].flattenToArrayOffset(uniform._array,16*i);_gl.uniformMatrix4fv(location,!1,uniform._array);break;case"t":if(texture=value,textureUnit=getTextureUnit(),_gl.uniform1i(location,textureUnit),!texture)continue;texture instanceof THREE.CubeTexture||Array.isArray(texture.image)&&6===texture.image.length?setCubeTexture(texture,textureUnit):texture instanceof THREE.WebGLRenderTargetCube?setCubeTextureDynamic(texture.texture,textureUnit):texture instanceof THREE.WebGLRenderTarget?_this.setTexture(texture.texture,textureUnit):_this.setTexture(texture,textureUnit);break;case"tv":void 0===uniform._array&&(uniform._array=[]);for(var i=0,il=uniform.value.length;il>i;i++)uniform._array[i]=getTextureUnit();_gl.uniform1iv(location,uniform._array);for(var i=0,il=uniform.value.length;il>i;i++)texture=uniform.value[i],textureUnit=uniform._array[i],texture&&(texture instanceof THREE.CubeTexture||texture.image instanceof Array&&6===texture.image.length?setCubeTexture(texture,textureUnit):texture instanceof THREE.WebGLRenderTarget?_this.setTexture(texture.texture,textureUnit):texture instanceof THREE.WebGLRenderTargetCube?setCubeTextureDynamic(texture.texture,textureUnit):_this.setTexture(texture,textureUnit));break;default:console.warn("THREE.WebGLRenderer: Unknown uniform type: "+type)}}}}function setColorLinear(array,offset,color,intensity){array[offset+0]=color.r*intensity,array[offset+1]=color.g*intensity,array[offset+2]=color.b*intensity}function setupLights(lights,camera){var l,ll,light,color,skyColor,groundColor,intensity,distance,r=0,g=0,b=0,zlights=_lights,viewMatrix=camera.matrixWorldInverse,dirColors=zlights.directional.colors,dirPositions=zlights.directional.positions,pointColors=zlights.point.colors,pointPositions=zlights.point.positions,pointDistances=zlights.point.distances,pointDecays=zlights.point.decays,spotColors=zlights.spot.colors,spotPositions=zlights.spot.positions,spotDistances=zlights.spot.distances,spotDirections=zlights.spot.directions,spotAnglesCos=zlights.spot.anglesCos,spotExponents=zlights.spot.exponents,spotDecays=zlights.spot.decays,hemiSkyColors=zlights.hemi.skyColors,hemiGroundColors=zlights.hemi.groundColors,hemiPositions=zlights.hemi.positions,dirLength=0,pointLength=0,spotLength=0,hemiLength=0,dirCount=0,pointCount=0,spotCount=0,hemiCount=0,dirOffset=0,pointOffset=0,spotOffset=0,hemiOffset=0;for(l=0,ll=lights.length;ll>l;l++)if(light=lights[l],color=light.color,intensity=light.intensity,distance=light.distance,light instanceof THREE.AmbientLight){if(!light.visible)continue;r+=color.r,g+=color.g,b+=color.b}else if(light instanceof THREE.DirectionalLight){if(dirCount+=1,!light.visible)continue;_direction.setFromMatrixPosition(light.matrixWorld),_vector3.setFromMatrixPosition(light.target.matrixWorld),_direction.sub(_vector3),_direction.transformDirection(viewMatrix),dirOffset=3*dirLength,dirPositions[dirOffset+0]=_direction.x,dirPositions[dirOffset+1]=_direction.y,dirPositions[dirOffset+2]=_direction.z,setColorLinear(dirColors,dirOffset,color,intensity),dirLength+=1}else if(light instanceof THREE.PointLight){if(pointCount+=1,!light.visible)continue;pointOffset=3*pointLength,setColorLinear(pointColors,pointOffset,color,intensity),_vector3.setFromMatrixPosition(light.matrixWorld),_vector3.applyMatrix4(viewMatrix),pointPositions[pointOffset+0]=_vector3.x,pointPositions[pointOffset+1]=_vector3.y,pointPositions[pointOffset+2]=_vector3.z,pointDistances[pointLength]=distance,pointDecays[pointLength]=0===light.distance?0:light.decay,pointLength+=1}else if(light instanceof THREE.SpotLight){if(spotCount+=1,!light.visible)continue;spotOffset=3*spotLength,setColorLinear(spotColors,spotOffset,color,intensity),_direction.setFromMatrixPosition(light.matrixWorld),_vector3.copy(_direction).applyMatrix4(viewMatrix),spotPositions[spotOffset+0]=_vector3.x,spotPositions[spotOffset+1]=_vector3.y,spotPositions[spotOffset+2]=_vector3.z,spotDistances[spotLength]=distance,_vector3.setFromMatrixPosition(light.target.matrixWorld),_direction.sub(_vector3),_direction.transformDirection(viewMatrix),spotDirections[spotOffset+0]=_direction.x,spotDirections[spotOffset+1]=_direction.y,spotDirections[spotOffset+2]=_direction.z,spotAnglesCos[spotLength]=Math.cos(light.angle),spotExponents[spotLength]=light.exponent,spotDecays[spotLength]=0===light.distance?0:light.decay,spotLength+=1}else if(light instanceof THREE.HemisphereLight){if(hemiCount+=1,!light.visible)continue;_direction.setFromMatrixPosition(light.matrixWorld),_direction.transformDirection(viewMatrix),hemiOffset=3*hemiLength,hemiPositions[hemiOffset+0]=_direction.x,hemiPositions[hemiOffset+1]=_direction.y,hemiPositions[hemiOffset+2]=_direction.z,skyColor=light.color,groundColor=light.groundColor,setColorLinear(hemiSkyColors,hemiOffset,skyColor,intensity),setColorLinear(hemiGroundColors,hemiOffset,groundColor,intensity),hemiLength+=1}for(l=3*dirLength,ll=Math.max(dirColors.length,3*dirCount);ll>l;l++)dirColors[l]=0;for(l=3*pointLength,ll=Math.max(pointColors.length,3*pointCount);ll>l;l++)pointColors[l]=0;for(l=3*spotLength,ll=Math.max(spotColors.length,3*spotCount);ll>l;l++)spotColors[l]=0;for(l=3*hemiLength,ll=Math.max(hemiSkyColors.length,3*hemiCount);ll>l;l++)hemiSkyColors[l]=0;for(l=3*hemiLength,ll=Math.max(hemiGroundColors.length,3*hemiCount);ll>l;l++)hemiGroundColors[l]=0;zlights.directional.length=dirLength,zlights.point.length=pointLength,zlights.spot.length=spotLength,zlights.hemi.length=hemiLength,zlights.ambient[0]=r,zlights.ambient[1]=g,zlights.ambient[2]=b}function setTextureParameters(textureType,texture,isImagePowerOfTwo){var extension;if(isImagePowerOfTwo?(_gl.texParameteri(textureType,_gl.TEXTURE_WRAP_S,paramThreeToGL(texture.wrapS)),_gl.texParameteri(textureType,_gl.TEXTURE_WRAP_T,paramThreeToGL(texture.wrapT)),_gl.texParameteri(textureType,_gl.TEXTURE_MAG_FILTER,paramThreeToGL(texture.magFilter)),_gl.texParameteri(textureType,_gl.TEXTURE_MIN_FILTER,paramThreeToGL(texture.minFilter))):(_gl.texParameteri(textureType,_gl.TEXTURE_WRAP_S,_gl.CLAMP_TO_EDGE),_gl.texParameteri(textureType,_gl.TEXTURE_WRAP_T,_gl.CLAMP_TO_EDGE),texture.wrapS===THREE.ClampToEdgeWrapping&&texture.wrapT===THREE.ClampToEdgeWrapping||console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.",texture),_gl.texParameteri(textureType,_gl.TEXTURE_MAG_FILTER,filterFallback(texture.magFilter)),_gl.texParameteri(textureType,_gl.TEXTURE_MIN_FILTER,filterFallback(texture.minFilter)),texture.minFilter!==THREE.NearestFilter&&texture.minFilter!==THREE.LinearFilter&&console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.",texture)),extension=extensions.get("EXT_texture_filter_anisotropic")){if(texture.type===THREE.FloatType&&null===extensions.get("OES_texture_float_linear"))return;if(texture.type===THREE.HalfFloatType&&null===extensions.get("OES_texture_half_float_linear"))return;(texture.anisotropy>1||properties.get(texture).__currentAnisotropy)&&(_gl.texParameterf(textureType,extension.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(texture.anisotropy,_this.getMaxAnisotropy())),properties.get(texture).__currentAnisotropy=texture.anisotropy)}}function uploadTexture(textureProperties,texture,slot){void 0===textureProperties.__webglInit&&(textureProperties.__webglInit=!0,texture.addEventListener("dispose",onTextureDispose),textureProperties.__webglTexture=_gl.createTexture(),_infoMemory.textures++),state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_2D,textureProperties.__webglTexture),_gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL,texture.flipY),_gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,texture.premultiplyAlpha),_gl.pixelStorei(_gl.UNPACK_ALIGNMENT,texture.unpackAlignment),texture.image=clampToMaxSize(texture.image,capabilities.maxTextureSize),textureNeedsPowerOfTwo(texture)&&isPowerOfTwo(texture.image)===!1&&(texture.image=makePowerOfTwo(texture.image));var image=texture.image,isImagePowerOfTwo=isPowerOfTwo(image),glFormat=paramThreeToGL(texture.format),glType=paramThreeToGL(texture.type);setTextureParameters(_gl.TEXTURE_2D,texture,isImagePowerOfTwo);var mipmap,mipmaps=texture.mipmaps;if(texture instanceof THREE.DataTexture)if(mipmaps.length>0&&isImagePowerOfTwo){for(var i=0,il=mipmaps.length;il>i;i++)mipmap=mipmaps[i],state.texImage2D(_gl.TEXTURE_2D,i,glFormat,mipmap.width,mipmap.height,0,glFormat,glType,mipmap.data);texture.generateMipmaps=!1}else state.texImage2D(_gl.TEXTURE_2D,0,glFormat,image.width,image.height,0,glFormat,glType,image.data);else if(texture instanceof THREE.CompressedTexture)for(var i=0,il=mipmaps.length;il>i;i++)mipmap=mipmaps[i],texture.format!==THREE.RGBAFormat&&texture.format!==THREE.RGBFormat?state.getCompressedTextureFormats().indexOf(glFormat)>-1?state.compressedTexImage2D(_gl.TEXTURE_2D,i,glFormat,mipmap.width,mipmap.height,0,mipmap.data):console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()"):state.texImage2D(_gl.TEXTURE_2D,i,glFormat,mipmap.width,mipmap.height,0,glFormat,glType,mipmap.data);else if(mipmaps.length>0&&isImagePowerOfTwo){for(var i=0,il=mipmaps.length;il>i;i++)mipmap=mipmaps[i],state.texImage2D(_gl.TEXTURE_2D,i,glFormat,glFormat,glType,mipmap);texture.generateMipmaps=!1}else state.texImage2D(_gl.TEXTURE_2D,0,glFormat,glFormat,glType,texture.image);texture.generateMipmaps&&isImagePowerOfTwo&&_gl.generateMipmap(_gl.TEXTURE_2D),textureProperties.__version=texture.version,texture.onUpdate&&texture.onUpdate(texture)}function clampToMaxSize(image,maxSize){if(image.width>maxSize||image.height>maxSize){var scale=maxSize/Math.max(image.width,image.height),canvas=document.createElement("canvas");canvas.width=Math.floor(image.width*scale),canvas.height=Math.floor(image.height*scale);var context=canvas.getContext("2d");return context.drawImage(image,0,0,image.width,image.height,0,0,canvas.width,canvas.height),console.warn("THREE.WebGLRenderer: image is too big ("+image.width+"x"+image.height+"). Resized to "+canvas.width+"x"+canvas.height,image),canvas}return image}function isPowerOfTwo(image){return THREE.Math.isPowerOfTwo(image.width)&&THREE.Math.isPowerOfTwo(image.height)}function textureNeedsPowerOfTwo(texture){return texture.wrapS!==THREE.ClampToEdgeWrapping||texture.wrapT!==THREE.ClampToEdgeWrapping?!0:texture.minFilter!==THREE.NearestFilter&&texture.minFilter!==THREE.LinearFilter}function makePowerOfTwo(image){if(image instanceof HTMLImageElement||image instanceof HTMLCanvasElement){var canvas=document.createElement("canvas");canvas.width=THREE.Math.nearestPowerOfTwo(image.width),canvas.height=THREE.Math.nearestPowerOfTwo(image.height);var context=canvas.getContext("2d");return context.drawImage(image,0,0,canvas.width,canvas.height),console.warn("THREE.WebGLRenderer: image is not power of two ("+image.width+"x"+image.height+"). Resized to "+canvas.width+"x"+canvas.height,image),canvas}return image}function setCubeTexture(texture,slot){var textureProperties=properties.get(texture);if(6===texture.image.length)if(texture.version>0&&textureProperties.__version!==texture.version){textureProperties.__image__webglTextureCube||(texture.addEventListener("dispose",onTextureDispose),textureProperties.__image__webglTextureCube=_gl.createTexture(),_infoMemory.textures++),state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_CUBE_MAP,textureProperties.__image__webglTextureCube),_gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL,texture.flipY);for(var isCompressed=texture instanceof THREE.CompressedTexture,isDataTexture=texture.image[0]instanceof THREE.DataTexture,cubeImage=[],i=0;6>i;i++)!_this.autoScaleCubemaps||isCompressed||isDataTexture?cubeImage[i]=isDataTexture?texture.image[i].image:texture.image[i]:cubeImage[i]=clampToMaxSize(texture.image[i],capabilities.maxCubemapSize);var image=cubeImage[0],isImagePowerOfTwo=isPowerOfTwo(image),glFormat=paramThreeToGL(texture.format),glType=paramThreeToGL(texture.type);setTextureParameters(_gl.TEXTURE_CUBE_MAP,texture,isImagePowerOfTwo);for(var i=0;6>i;i++)if(isCompressed)for(var mipmap,mipmaps=cubeImage[i].mipmaps,j=0,jl=mipmaps.length;jl>j;j++)mipmap=mipmaps[j],texture.format!==THREE.RGBAFormat&&texture.format!==THREE.RGBFormat?state.getCompressedTextureFormats().indexOf(glFormat)>-1?state.compressedTexImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,j,glFormat,mipmap.width,mipmap.height,0,mipmap.data):console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setCubeTexture()"):state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,j,glFormat,mipmap.width,mipmap.height,0,glFormat,glType,mipmap.data);else isDataTexture?state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,glFormat,cubeImage[i].width,cubeImage[i].height,0,glFormat,glType,cubeImage[i].data):state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,glFormat,glFormat,glType,cubeImage[i]);texture.generateMipmaps&&isImagePowerOfTwo&&_gl.generateMipmap(_gl.TEXTURE_CUBE_MAP),textureProperties.__version=texture.version,texture.onUpdate&&texture.onUpdate(texture)}else state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_CUBE_MAP,textureProperties.__image__webglTextureCube)}function setCubeTextureDynamic(texture,slot){state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_CUBE_MAP,properties.get(texture).__webglTexture)}function setupFrameBuffer(framebuffer,renderTarget,textureTarget){_gl.bindFramebuffer(_gl.FRAMEBUFFER,framebuffer),_gl.framebufferTexture2D(_gl.FRAMEBUFFER,_gl.COLOR_ATTACHMENT0,textureTarget,properties.get(renderTarget.texture).__webglTexture,0)}function setupRenderBuffer(renderbuffer,renderTarget){_gl.bindRenderbuffer(_gl.RENDERBUFFER,renderbuffer),renderTarget.depthBuffer&&!renderTarget.stencilBuffer?(_gl.renderbufferStorage(_gl.RENDERBUFFER,_gl.DEPTH_COMPONENT16,renderTarget.width,renderTarget.height),_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER,_gl.DEPTH_ATTACHMENT,_gl.RENDERBUFFER,renderbuffer)):renderTarget.depthBuffer&&renderTarget.stencilBuffer?(_gl.renderbufferStorage(_gl.RENDERBUFFER,_gl.DEPTH_STENCIL,renderTarget.width,renderTarget.height),_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER,_gl.DEPTH_STENCIL_ATTACHMENT,_gl.RENDERBUFFER,renderbuffer)):_gl.renderbufferStorage(_gl.RENDERBUFFER,_gl.RGBA4,renderTarget.width,renderTarget.height)}function updateRenderTargetMipmap(renderTarget){var target=renderTarget instanceof THREE.WebGLRenderTargetCube?_gl.TEXTURE_CUBE_MAP:_gl.TEXTURE_2D,texture=properties.get(renderTarget.texture).__webglTexture;state.bindTexture(target,texture),_gl.generateMipmap(target),state.bindTexture(target,null)}function filterFallback(f){return f===THREE.NearestFilter||f===THREE.NearestMipMapNearestFilter||f===THREE.NearestMipMapLinearFilter?_gl.NEAREST:_gl.LINEAR}function paramThreeToGL(p){var extension;if(p===THREE.RepeatWrapping)return _gl.REPEAT;if(p===THREE.ClampToEdgeWrapping)return _gl.CLAMP_TO_EDGE;if(p===THREE.MirroredRepeatWrapping)return _gl.MIRRORED_REPEAT;if(p===THREE.NearestFilter)return _gl.NEAREST;if(p===THREE.NearestMipMapNearestFilter)return _gl.NEAREST_MIPMAP_NEAREST;if(p===THREE.NearestMipMapLinearFilter)return _gl.NEAREST_MIPMAP_LINEAR;if(p===THREE.LinearFilter)return _gl.LINEAR;if(p===THREE.LinearMipMapNearestFilter)return _gl.LINEAR_MIPMAP_NEAREST;if(p===THREE.LinearMipMapLinearFilter)return _gl.LINEAR_MIPMAP_LINEAR;if(p===THREE.UnsignedByteType)return _gl.UNSIGNED_BYTE;if(p===THREE.UnsignedShort4444Type)return _gl.UNSIGNED_SHORT_4_4_4_4;if(p===THREE.UnsignedShort5551Type)return _gl.UNSIGNED_SHORT_5_5_5_1;if(p===THREE.UnsignedShort565Type)return _gl.UNSIGNED_SHORT_5_6_5;if(p===THREE.ByteType)return _gl.BYTE;if(p===THREE.ShortType)return _gl.SHORT;if(p===THREE.UnsignedShortType)return _gl.UNSIGNED_SHORT;if(p===THREE.IntType)return _gl.INT;if(p===THREE.UnsignedIntType)return _gl.UNSIGNED_INT;if(p===THREE.FloatType)return _gl.FLOAT;if(extension=extensions.get("OES_texture_half_float"),null!==extension&&p===THREE.HalfFloatType)return extension.HALF_FLOAT_OES;if(p===THREE.AlphaFormat)return _gl.ALPHA;if(p===THREE.RGBFormat)return _gl.RGB;if(p===THREE.RGBAFormat)return _gl.RGBA;if(p===THREE.LuminanceFormat)return _gl.LUMINANCE;if(p===THREE.LuminanceAlphaFormat)return _gl.LUMINANCE_ALPHA;if(p===THREE.AddEquation)return _gl.FUNC_ADD;if(p===THREE.SubtractEquation)return _gl.FUNC_SUBTRACT;if(p===THREE.ReverseSubtractEquation)return _gl.FUNC_REVERSE_SUBTRACT;if(p===THREE.ZeroFactor)return _gl.ZERO;if(p===THREE.OneFactor)return _gl.ONE;if(p===THREE.SrcColorFactor)return _gl.SRC_COLOR;if(p===THREE.OneMinusSrcColorFactor)return _gl.ONE_MINUS_SRC_COLOR;if(p===THREE.SrcAlphaFactor)return _gl.SRC_ALPHA;if(p===THREE.OneMinusSrcAlphaFactor)return _gl.ONE_MINUS_SRC_ALPHA;if(p===THREE.DstAlphaFactor)return _gl.DST_ALPHA;if(p===THREE.OneMinusDstAlphaFactor)return _gl.ONE_MINUS_DST_ALPHA;if(p===THREE.DstColorFactor)return _gl.DST_COLOR;if(p===THREE.OneMinusDstColorFactor)return _gl.ONE_MINUS_DST_COLOR;if(p===THREE.SrcAlphaSaturateFactor)return _gl.SRC_ALPHA_SATURATE;if(extension=extensions.get("WEBGL_compressed_texture_s3tc"),null!==extension){if(p===THREE.RGB_S3TC_DXT1_Format)return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;if(p===THREE.RGBA_S3TC_DXT1_Format)return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(p===THREE.RGBA_S3TC_DXT3_Format)return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(p===THREE.RGBA_S3TC_DXT5_Format)return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT}if(extension=extensions.get("WEBGL_compressed_texture_pvrtc"),null!==extension){if(p===THREE.RGB_PVRTC_4BPPV1_Format)return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(p===THREE.RGB_PVRTC_2BPPV1_Format)return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(p===THREE.RGBA_PVRTC_4BPPV1_Format)return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(p===THREE.RGBA_PVRTC_2BPPV1_Format)return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}if(extension=extensions.get("EXT_blend_minmax"),null!==extension){if(p===THREE.MinEquation)return extension.MIN_EXT;if(p===THREE.MaxEquation)return extension.MAX_EXT}return 0}console.log("THREE.WebGLRenderer",THREE.REVISION),parameters=parameters||{};var _canvas=void 0!==parameters.canvas?parameters.canvas:document.createElement("canvas"),_context=void 0!==parameters.context?parameters.context:null,_width=_canvas.width,_height=_canvas.height,pixelRatio=1,_alpha=void 0!==parameters.alpha?parameters.alpha:!1,_depth=void 0!==parameters.depth?parameters.depth:!0,_stencil=void 0!==parameters.stencil?parameters.stencil:!0,_antialias=void 0!==parameters.antialias?parameters.antialias:!1,_premultipliedAlpha=void 0!==parameters.premultipliedAlpha?parameters.premultipliedAlpha:!0,_preserveDrawingBuffer=void 0!==parameters.preserveDrawingBuffer?parameters.preserveDrawingBuffer:!1,_clearColor=new THREE.Color(0),_clearAlpha=0,lights=[],opaqueObjects=[],opaqueObjectsLastIndex=-1,transparentObjects=[],transparentObjectsLastIndex=-1,morphInfluences=new Float32Array(8),sprites=[],lensFlares=[];this.domElement=_canvas,this.context=null,this.autoClear=!0,this.autoClearColor=!0,this.autoClearDepth=!0,this.autoClearStencil=!0,this.sortObjects=!0,this.gammaFactor=2,this.gammaInput=!1,this.gammaOutput=!1,this.maxMorphTargets=8,this.maxMorphNormals=4,this.autoScaleCubemaps=!0;var _this=this,_currentProgram=null,_currentFramebuffer=null,_currentMaterialId=-1,_currentGeometryProgram="",_currentCamera=null,_usedTextureUnits=0,_viewportX=0,_viewportY=0,_viewportWidth=_canvas.width,_viewportHeight=_canvas.height,_currentWidth=0,_currentHeight=0,_frustum=new THREE.Frustum,_projScreenMatrix=new THREE.Matrix4,_vector3=new THREE.Vector3,_direction=new THREE.Vector3,_lightsNeedUpdate=!0,_lights={ambient:[0,0,0],directional:{length:0,colors:[],positions:[]},point:{length:0,colors:[],positions:[],distances:[],decays:[]},spot:{length:0,colors:[],positions:[],distances:[],directions:[],anglesCos:[],exponents:[],decays:[]},hemi:{length:0,skyColors:[],groundColors:[],positions:[]}},_infoMemory={geometries:0,textures:0},_infoRender={calls:0,vertices:0,faces:0,points:0};this.info={render:_infoRender,memory:_infoMemory,programs:null};var _gl;try{var attributes={alpha:_alpha,depth:_depth,stencil:_stencil,antialias:_antialias,premultipliedAlpha:_premultipliedAlpha,preserveDrawingBuffer:_preserveDrawingBuffer};if(_gl=_context||_canvas.getContext("webgl",attributes)||_canvas.getContext("experimental-webgl",attributes),null===_gl)throw null!==_canvas.getContext("webgl")?"Error creating WebGL context with your selected attributes.":"Error creating WebGL context.";_canvas.addEventListener("webglcontextlost",onContextLost,!1)}catch(error){console.error("THREE.WebGLRenderer: "+error)}var extensions=new THREE.WebGLExtensions(_gl);extensions.get("OES_texture_float"),extensions.get("OES_texture_float_linear"),extensions.get("OES_texture_half_float"),extensions.get("OES_texture_half_float_linear"),extensions.get("OES_standard_derivatives"),extensions.get("ANGLE_instanced_arrays"),extensions.get("OES_element_index_uint")&&(THREE.BufferGeometry.MaxIndex=4294967296);var capabilities=new THREE.WebGLCapabilities(_gl,extensions,parameters),state=new THREE.WebGLState(_gl,extensions,paramThreeToGL),properties=new THREE.WebGLProperties,objects=new THREE.WebGLObjects(_gl,properties,this.info),programCache=new THREE.WebGLPrograms(this,capabilities);this.info.programs=programCache.programs;var bufferRenderer=new THREE.WebGLBufferRenderer(_gl,extensions,_infoRender),indexedBufferRenderer=new THREE.WebGLIndexedBufferRenderer(_gl,extensions,_infoRender);setDefaultGLState(),this.context=_gl,this.capabilities=capabilities,this.extensions=extensions,this.state=state;var shadowMap=new THREE.WebGLShadowMap(this,lights,objects);this.shadowMap=shadowMap;var spritePlugin=new THREE.SpritePlugin(this,sprites),lensFlarePlugin=new THREE.LensFlarePlugin(this,lensFlares);this.getContext=function(){return _gl},this.getContextAttributes=function(){return _gl.getContextAttributes()},this.forceContextLoss=function(){extensions.get("WEBGL_lose_context").loseContext()},this.getMaxAnisotropy=function(){var value;return function(){if(void 0!==value)return value;var extension=extensions.get("EXT_texture_filter_anisotropic");return value=null!==extension?_gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT):0}}(),this.getPrecision=function(){return capabilities.precision},this.getPixelRatio=function(){return pixelRatio},this.setPixelRatio=function(value){void 0!==value&&(pixelRatio=value)},this.getSize=function(){return{width:_width,height:_height}},this.setSize=function(width,height,updateStyle){_width=width,_height=height,_canvas.width=width*pixelRatio,_canvas.height=height*pixelRatio,updateStyle!==!1&&(_canvas.style.width=width+"px",_canvas.style.height=height+"px"),this.setViewport(0,0,width,height)},this.setViewport=function(x,y,width,height){_viewportX=x*pixelRatio,_viewportY=y*pixelRatio,_viewportWidth=width*pixelRatio,_viewportHeight=height*pixelRatio,_gl.viewport(_viewportX,_viewportY,_viewportWidth,_viewportHeight)},this.getViewport=function(dimensions){dimensions.x=_viewportX/pixelRatio,dimensions.y=_viewportY/pixelRatio,dimensions.z=_viewportWidth/pixelRatio,dimensions.w=_viewportHeight/pixelRatio},this.setScissor=function(x,y,width,height){_gl.scissor(x*pixelRatio,y*pixelRatio,width*pixelRatio,height*pixelRatio)},this.enableScissorTest=function(boolean){state.setScissorTest(boolean)},this.getClearColor=function(){return _clearColor},this.setClearColor=function(color,alpha){_clearColor.set(color),_clearAlpha=void 0!==alpha?alpha:1,glClearColor(_clearColor.r,_clearColor.g,_clearColor.b,_clearAlpha)},this.getClearAlpha=function(){return _clearAlpha},this.setClearAlpha=function(alpha){_clearAlpha=alpha,glClearColor(_clearColor.r,_clearColor.g,_clearColor.b,_clearAlpha)},this.clear=function(color,depth,stencil){var bits=0;(void 0===color||color)&&(bits|=_gl.COLOR_BUFFER_BIT),(void 0===depth||depth)&&(bits|=_gl.DEPTH_BUFFER_BIT),(void 0===stencil||stencil)&&(bits|=_gl.STENCIL_BUFFER_BIT),_gl.clear(bits)},this.clearColor=function(){_gl.clear(_gl.COLOR_BUFFER_BIT)},this.clearDepth=function(){_gl.clear(_gl.DEPTH_BUFFER_BIT)},this.clearStencil=function(){_gl.clear(_gl.STENCIL_BUFFER_BIT)},this.clearTarget=function(renderTarget,color,depth,stencil){this.setRenderTarget(renderTarget),this.clear(color,depth,stencil)},this.resetGLState=resetGLState,this.dispose=function(){_canvas.removeEventListener("webglcontextlost",onContextLost,!1)},this.renderBufferImmediate=function(object,program,material){state.initAttributes();var buffers=properties.get(object);object.hasPositions&&!buffers.position&&(buffers.position=_gl.createBuffer()),
+object.hasNormals&&!buffers.normal&&(buffers.normal=_gl.createBuffer()),object.hasUvs&&!buffers.uv&&(buffers.uv=_gl.createBuffer()),object.hasColors&&!buffers.color&&(buffers.color=_gl.createBuffer());var attributes=program.getAttributes();if(object.hasPositions&&(_gl.bindBuffer(_gl.ARRAY_BUFFER,buffers.position),_gl.bufferData(_gl.ARRAY_BUFFER,object.positionArray,_gl.DYNAMIC_DRAW),state.enableAttribute(attributes.position),_gl.vertexAttribPointer(attributes.position,3,_gl.FLOAT,!1,0,0)),object.hasNormals){if(_gl.bindBuffer(_gl.ARRAY_BUFFER,buffers.normal),"MeshPhongMaterial"!==material.type&&material.shading===THREE.FlatShading)for(var i=0,l=3*object.count;l>i;i+=9){var array=object.normalArray,nx=(array[i+0]+array[i+3]+array[i+6])/3,ny=(array[i+1]+array[i+4]+array[i+7])/3,nz=(array[i+2]+array[i+5]+array[i+8])/3;array[i+0]=nx,array[i+1]=ny,array[i+2]=nz,array[i+3]=nx,array[i+4]=ny,array[i+5]=nz,array[i+6]=nx,array[i+7]=ny,array[i+8]=nz}_gl.bufferData(_gl.ARRAY_BUFFER,object.normalArray,_gl.DYNAMIC_DRAW),state.enableAttribute(attributes.normal),_gl.vertexAttribPointer(attributes.normal,3,_gl.FLOAT,!1,0,0)}object.hasUvs&&material.map&&(_gl.bindBuffer(_gl.ARRAY_BUFFER,buffers.uv),_gl.bufferData(_gl.ARRAY_BUFFER,object.uvArray,_gl.DYNAMIC_DRAW),state.enableAttribute(attributes.uv),_gl.vertexAttribPointer(attributes.uv,2,_gl.FLOAT,!1,0,0)),object.hasColors&&material.vertexColors!==THREE.NoColors&&(_gl.bindBuffer(_gl.ARRAY_BUFFER,buffers.color),_gl.bufferData(_gl.ARRAY_BUFFER,object.colorArray,_gl.DYNAMIC_DRAW),state.enableAttribute(attributes.color),_gl.vertexAttribPointer(attributes.color,3,_gl.FLOAT,!1,0,0)),state.disableUnusedAttributes(),_gl.drawArrays(_gl.TRIANGLES,0,object.count),object.count=0},this.renderBufferDirect=function(camera,lights,fog,geometry,material,object,group){setMaterial(material);var program=setProgram(camera,lights,fog,material,object),updateBuffers=!1,geometryProgram=geometry.id+"_"+program.id+"_"+material.wireframe;geometryProgram!==_currentGeometryProgram&&(_currentGeometryProgram=geometryProgram,updateBuffers=!0);var morphTargetInfluences=object.morphTargetInfluences;if(void 0!==morphTargetInfluences){for(var activeInfluences=[],i=0,l=morphTargetInfluences.length;l>i;i++){var influence=morphTargetInfluences[i];activeInfluences.push([influence,i])}activeInfluences.sort(numericalSort),activeInfluences.length>8&&(activeInfluences.length=8);for(var morphAttributes=geometry.morphAttributes,i=0,l=activeInfluences.length;l>i;i++){var influence=activeInfluences[i];if(morphInfluences[i]=influence[0],0!==influence[0]){var index=influence[1];material.morphTargets===!0&&morphAttributes.position&&geometry.addAttribute("morphTarget"+i,morphAttributes.position[index]),material.morphNormals===!0&&morphAttributes.normal&&geometry.addAttribute("morphNormal"+i,morphAttributes.normal[index])}else material.morphTargets===!0&&geometry.removeAttribute("morphTarget"+i),material.morphNormals===!0&&geometry.removeAttribute("morphNormal"+i)}var uniforms=program.getUniforms();null!==uniforms.morphTargetInfluences&&_gl.uniform1fv(uniforms.morphTargetInfluences,morphInfluences),updateBuffers=!0}var index=geometry.index,position=geometry.attributes.position;material.wireframe===!0&&(index=objects.getWireframeAttribute(geometry));var renderer;null!==index?(renderer=indexedBufferRenderer,renderer.setIndex(index)):renderer=bufferRenderer,updateBuffers&&(setupVertexAttributes(material,program,geometry),null!==index&&_gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER,objects.getAttributeBuffer(index)));var dataStart=0,dataCount=1/0;null!==index?dataCount=index.count:void 0!==position&&(dataCount=position.count);var rangeStart=geometry.drawRange.start,rangeCount=geometry.drawRange.count,groupStart=null!==group?group.start:0,groupCount=null!==group?group.count:1/0,drawStart=Math.max(dataStart,rangeStart,groupStart),drawEnd=Math.min(dataStart+dataCount,rangeStart+rangeCount,groupStart+groupCount)-1,drawCount=Math.max(0,drawEnd-drawStart+1);if(object instanceof THREE.Mesh)material.wireframe===!0?(state.setLineWidth(material.wireframeLinewidth*pixelRatio),renderer.setMode(_gl.LINES)):renderer.setMode(_gl.TRIANGLES),geometry instanceof THREE.InstancedBufferGeometry&&geometry.maxInstancedCount>0?renderer.renderInstances(geometry):renderer.render(drawStart,drawCount);else if(object instanceof THREE.Line){var lineWidth=material.linewidth;void 0===lineWidth&&(lineWidth=1),state.setLineWidth(lineWidth*pixelRatio),object instanceof THREE.LineSegments?renderer.setMode(_gl.LINES):renderer.setMode(_gl.LINE_STRIP),renderer.render(drawStart,drawCount)}else object instanceof THREE.Points&&(renderer.setMode(_gl.POINTS),renderer.render(drawStart,drawCount))},this.render=function(scene,camera,renderTarget,forceClear){if(camera instanceof THREE.Camera==!1)return void console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.");var fog=scene.fog;if(_currentGeometryProgram="",_currentMaterialId=-1,_currentCamera=null,_lightsNeedUpdate=!0,scene.autoUpdate===!0&&scene.updateMatrixWorld(),null===camera.parent&&camera.updateMatrixWorld(),camera.matrixWorldInverse.getInverse(camera.matrixWorld),_projScreenMatrix.multiplyMatrices(camera.projectionMatrix,camera.matrixWorldInverse),_frustum.setFromMatrix(_projScreenMatrix),lights.length=0,opaqueObjectsLastIndex=-1,transparentObjectsLastIndex=-1,sprites.length=0,lensFlares.length=0,projectObject(scene,camera),opaqueObjects.length=opaqueObjectsLastIndex+1,transparentObjects.length=transparentObjectsLastIndex+1,_this.sortObjects===!0&&(opaqueObjects.sort(painterSortStable),transparentObjects.sort(reversePainterSortStable)),shadowMap.render(scene),_infoRender.calls=0,_infoRender.vertices=0,_infoRender.faces=0,_infoRender.points=0,this.setRenderTarget(renderTarget),(this.autoClear||forceClear)&&this.clear(this.autoClearColor,this.autoClearDepth,this.autoClearStencil),scene.overrideMaterial){var overrideMaterial=scene.overrideMaterial;renderObjects(opaqueObjects,camera,lights,fog,overrideMaterial),renderObjects(transparentObjects,camera,lights,fog,overrideMaterial)}else state.setBlending(THREE.NoBlending),renderObjects(opaqueObjects,camera,lights,fog),renderObjects(transparentObjects,camera,lights,fog);if(spritePlugin.render(scene,camera),lensFlarePlugin.render(scene,camera,_currentWidth,_currentHeight),renderTarget){var texture=renderTarget.texture,isTargetPowerOfTwo=isPowerOfTwo(renderTarget);texture.generateMipmaps&&isTargetPowerOfTwo&&texture.minFilter!==THREE.NearestFilter&&texture.minFilter!==THREE.LinearFilter&&updateRenderTargetMipmap(renderTarget)}state.setDepthTest(!0),state.setDepthWrite(!0),state.setColorWrite(!0)},this.setFaceCulling=function(cullFace,frontFaceDirection){cullFace===THREE.CullFaceNone?state.disable(_gl.CULL_FACE):(frontFaceDirection===THREE.FrontFaceDirectionCW?_gl.frontFace(_gl.CW):_gl.frontFace(_gl.CCW),cullFace===THREE.CullFaceBack?_gl.cullFace(_gl.BACK):cullFace===THREE.CullFaceFront?_gl.cullFace(_gl.FRONT):_gl.cullFace(_gl.FRONT_AND_BACK),state.enable(_gl.CULL_FACE))},this.setTexture=function(texture,slot){var textureProperties=properties.get(texture);if(texture.version>0&&textureProperties.__version!==texture.version){var image=texture.image;return void 0===image?void console.warn("THREE.WebGLRenderer: Texture marked for update but image is undefined",texture):image.complete===!1?void console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete",texture):void uploadTexture(textureProperties,texture,slot)}state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_2D,textureProperties.__webglTexture)},this.setRenderTarget=function(renderTarget){var isCube=renderTarget instanceof THREE.WebGLRenderTargetCube;if(renderTarget&&void 0===properties.get(renderTarget).__webglFramebuffer){var renderTargetProperties=properties.get(renderTarget),textureProperties=properties.get(renderTarget.texture);void 0===renderTarget.depthBuffer&&(renderTarget.depthBuffer=!0),void 0===renderTarget.stencilBuffer&&(renderTarget.stencilBuffer=!0),renderTarget.addEventListener("dispose",onRenderTargetDispose),textureProperties.__webglTexture=_gl.createTexture(),_infoMemory.textures++;var isTargetPowerOfTwo=isPowerOfTwo(renderTarget),glFormat=paramThreeToGL(renderTarget.texture.format),glType=paramThreeToGL(renderTarget.texture.type);if(isCube){renderTargetProperties.__webglFramebuffer=[],renderTargetProperties.__webglRenderbuffer=[],state.bindTexture(_gl.TEXTURE_CUBE_MAP,textureProperties.__webglTexture),setTextureParameters(_gl.TEXTURE_CUBE_MAP,renderTarget.texture,isTargetPowerOfTwo);for(var i=0;6>i;i++)renderTargetProperties.__webglFramebuffer[i]=_gl.createFramebuffer(),renderTargetProperties.__webglRenderbuffer[i]=_gl.createRenderbuffer(),state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,glFormat,renderTarget.width,renderTarget.height,0,glFormat,glType,null),setupFrameBuffer(renderTargetProperties.__webglFramebuffer[i],renderTarget,_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i),setupRenderBuffer(renderTargetProperties.__webglRenderbuffer[i],renderTarget);renderTarget.texture.generateMipmaps&&isTargetPowerOfTwo&&_gl.generateMipmap(_gl.TEXTURE_CUBE_MAP)}else renderTargetProperties.__webglFramebuffer=_gl.createFramebuffer(),renderTarget.shareDepthFrom?renderTargetProperties.__webglRenderbuffer=renderTarget.shareDepthFrom.__webglRenderbuffer:renderTargetProperties.__webglRenderbuffer=_gl.createRenderbuffer(),state.bindTexture(_gl.TEXTURE_2D,textureProperties.__webglTexture),setTextureParameters(_gl.TEXTURE_2D,renderTarget.texture,isTargetPowerOfTwo),state.texImage2D(_gl.TEXTURE_2D,0,glFormat,renderTarget.width,renderTarget.height,0,glFormat,glType,null),setupFrameBuffer(renderTargetProperties.__webglFramebuffer,renderTarget,_gl.TEXTURE_2D),renderTarget.shareDepthFrom?renderTarget.depthBuffer&&!renderTarget.stencilBuffer?_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER,_gl.DEPTH_ATTACHMENT,_gl.RENDERBUFFER,renderTargetProperties.__webglRenderbuffer):renderTarget.depthBuffer&&renderTarget.stencilBuffer&&_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER,_gl.DEPTH_STENCIL_ATTACHMENT,_gl.RENDERBUFFER,renderTargetProperties.__webglRenderbuffer):setupRenderBuffer(renderTargetProperties.__webglRenderbuffer,renderTarget),renderTarget.texture.generateMipmaps&&isTargetPowerOfTwo&&_gl.generateMipmap(_gl.TEXTURE_2D);isCube?state.bindTexture(_gl.TEXTURE_CUBE_MAP,null):state.bindTexture(_gl.TEXTURE_2D,null),_gl.bindRenderbuffer(_gl.RENDERBUFFER,null),_gl.bindFramebuffer(_gl.FRAMEBUFFER,null)}var framebuffer,width,height,vx,vy;if(renderTarget){var renderTargetProperties=properties.get(renderTarget);framebuffer=isCube?renderTargetProperties.__webglFramebuffer[renderTarget.activeCubeFace]:renderTargetProperties.__webglFramebuffer,width=renderTarget.width,height=renderTarget.height,vx=0,vy=0}else framebuffer=null,width=_viewportWidth,height=_viewportHeight,vx=_viewportX,vy=_viewportY;if(framebuffer!==_currentFramebuffer&&(_gl.bindFramebuffer(_gl.FRAMEBUFFER,framebuffer),_gl.viewport(vx,vy,width,height),_currentFramebuffer=framebuffer),isCube){var textureProperties=properties.get(renderTarget.texture);_gl.framebufferTexture2D(_gl.FRAMEBUFFER,_gl.COLOR_ATTACHMENT0,_gl.TEXTURE_CUBE_MAP_POSITIVE_X+renderTarget.activeCubeFace,textureProperties.__webglTexture,0)}_currentWidth=width,_currentHeight=height},this.readRenderTargetPixels=function(renderTarget,x,y,width,height,buffer){if(renderTarget instanceof THREE.WebGLRenderTarget==!1)return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");var framebuffer=properties.get(renderTarget).__webglFramebuffer;if(framebuffer){var restore=!1;framebuffer!==_currentFramebuffer&&(_gl.bindFramebuffer(_gl.FRAMEBUFFER,framebuffer),restore=!0);try{var texture=renderTarget.texture;if(texture.format!==THREE.RGBAFormat&¶mThreeToGL(texture.format)!==_gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_FORMAT))return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.");if(!(texture.type===THREE.UnsignedByteType||paramThreeToGL(texture.type)===_gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_TYPE)||texture.type===THREE.FloatType&&extensions.get("WEBGL_color_buffer_float")||texture.type===THREE.HalfFloatType&&extensions.get("EXT_color_buffer_half_float")))return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.");_gl.checkFramebufferStatus(_gl.FRAMEBUFFER)===_gl.FRAMEBUFFER_COMPLETE?_gl.readPixels(x,y,width,height,paramThreeToGL(texture.format),paramThreeToGL(texture.type),buffer):console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.")}finally{restore&&_gl.bindFramebuffer(_gl.FRAMEBUFFER,_currentFramebuffer)}}},this.supportsFloatTextures=function(){return console.warn("THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( 'OES_texture_float' )."),extensions.get("OES_texture_float")},this.supportsHalfFloatTextures=function(){return console.warn("THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( 'OES_texture_half_float' )."),extensions.get("OES_texture_half_float")},this.supportsStandardDerivatives=function(){return console.warn("THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( 'OES_standard_derivatives' )."),extensions.get("OES_standard_derivatives")},this.supportsCompressedTextureS3TC=function(){return console.warn("THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( 'WEBGL_compressed_texture_s3tc' )."),extensions.get("WEBGL_compressed_texture_s3tc")},this.supportsCompressedTexturePVRTC=function(){return console.warn("THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( 'WEBGL_compressed_texture_pvrtc' )."),extensions.get("WEBGL_compressed_texture_pvrtc")},this.supportsBlendMinMax=function(){return console.warn("THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( 'EXT_blend_minmax' )."),extensions.get("EXT_blend_minmax")},this.supportsVertexTextures=function(){return capabilities.vertexTextures},this.supportsInstancedArrays=function(){return console.warn("THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( 'ANGLE_instanced_arrays' )."),extensions.get("ANGLE_instanced_arrays")},this.initMaterial=function(){console.warn("THREE.WebGLRenderer: .initMaterial() has been removed.")},this.addPrePlugin=function(){console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed.")},this.addPostPlugin=function(){console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed.")},this.updateShadowMap=function(){console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed.")},Object.defineProperties(this,{shadowMapEnabled:{get:function(){return shadowMap.enabled},set:function(value){console.warn("THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled."),shadowMap.enabled=value}},shadowMapType:{get:function(){return shadowMap.type},set:function(value){console.warn("THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type."),shadowMap.type=value}},shadowMapCullFace:{get:function(){return shadowMap.cullFace},set:function(value){console.warn("THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace."),shadowMap.cullFace=value}},shadowMapDebug:{get:function(){return shadowMap.debug},set:function(value){console.warn("THREE.WebGLRenderer: .shadowMapDebug is now .shadowMap.debug."),shadowMap.debug=value}}})},THREE.WebGLRenderTarget=function(width,height,options){this.uuid=THREE.Math.generateUUID(),this.width=width,this.height=height,options=options||{},void 0===options.minFilter&&(options.minFilter=THREE.LinearFilter),this.texture=new THREE.Texture(void 0,void 0,options.wrapS,options.wrapT,options.magFilter,options.minFilter,options.format,options.type,options.anisotropy),this.depthBuffer=void 0!==options.depthBuffer?options.depthBuffer:!0,this.stencilBuffer=void 0!==options.stencilBuffer?options.stencilBuffer:!0,this.shareDepthFrom=void 0!==options.shareDepthFrom?options.shareDepthFrom:null},THREE.WebGLRenderTarget.prototype={constructor:THREE.WebGLRenderTarget,get wrapS(){return console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."),this.texture.wrapS},set wrapS(value){console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."),this.texture.wrapS=value},get wrapT(){return console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."),this.texture.wrapT},set wrapT(value){console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."),this.texture.wrapT=value},get magFilter(){return console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."),this.texture.magFilter},set magFilter(value){console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."),this.texture.magFilter=value},get minFilter(){return console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."),this.texture.minFilter},set minFilter(value){console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."),this.texture.minFilter=value},get anisotropy(){return console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."),this.texture.anisotropy},set anisotropy(value){console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."),this.texture.anisotropy=value},get offset(){return console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."),this.texture.offset},set offset(value){console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."),this.texture.offset=value},get repeat(){return console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."),this.texture.repeat},set repeat(value){console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."),this.texture.repeat=value},get format(){return console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."),this.texture.format},set format(value){console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."),this.texture.format=value},get type(){return console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."),this.texture.type},set type(value){console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."),this.texture.type=value},get generateMipmaps(){return console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."),this.texture.generateMipmaps},set generateMipmaps(value){console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."),this.texture.generateMipmaps=value},setSize:function(width,height){this.width===width&&this.height===height||(this.width=width,this.height=height,this.dispose())},clone:function(){return(new this.constructor).copy(this)},copy:function(source){return this.width=source.width,this.height=source.height,this.texture=source.texture.clone(),this.depthBuffer=source.depthBuffer,this.stencilBuffer=source.stencilBuffer,this.shareDepthFrom=source.shareDepthFrom,this},dispose:function(){this.dispatchEvent({type:"dispose"})}},THREE.EventDispatcher.prototype.apply(THREE.WebGLRenderTarget.prototype),THREE.WebGLRenderTargetCube=function(width,height,options){THREE.WebGLRenderTarget.call(this,width,height,options),this.activeCubeFace=0},THREE.WebGLRenderTargetCube.prototype=Object.create(THREE.WebGLRenderTarget.prototype),THREE.WebGLRenderTargetCube.prototype.constructor=THREE.WebGLRenderTargetCube,THREE.WebGLBufferRenderer=function(_gl,extensions,_infoRender){function setMode(value){mode=value}function render(start,count){_gl.drawArrays(mode,start,count),_infoRender.calls++,_infoRender.vertices+=count,mode===_gl.TRIANGLES&&(_infoRender.faces+=count/3)}function renderInstances(geometry){var extension=extensions.get("ANGLE_instanced_arrays");if(null===extension)return void console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.");var position=geometry.attributes.position;position instanceof THREE.InterleavedBufferAttribute?extension.drawArraysInstancedANGLE(mode,0,position.data.count,geometry.maxInstancedCount):extension.drawArraysInstancedANGLE(mode,0,position.count,geometry.maxInstancedCount)}var mode;this.setMode=setMode,this.render=render,this.renderInstances=renderInstances},THREE.WebGLIndexedBufferRenderer=function(_gl,extensions,_infoRender){function setMode(value){mode=value}function setIndex(index){index.array instanceof Uint32Array&&extensions.get("OES_element_index_uint")?(type=_gl.UNSIGNED_INT,size=4):(type=_gl.UNSIGNED_SHORT,size=2)}function render(start,count){_gl.drawElements(mode,count,type,start*size),_infoRender.calls++,_infoRender.vertices+=count,mode===_gl.TRIANGLES&&(_infoRender.faces+=count/3)}function renderInstances(geometry){var extension=extensions.get("ANGLE_instanced_arrays");if(null===extension)return void console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.");var index=geometry.index;extension.drawElementsInstancedANGLE(mode,index.array.length,type,0,geometry.maxInstancedCount)}var mode,type,size;this.setMode=setMode,this.setIndex=setIndex,this.render=render,this.renderInstances=renderInstances},THREE.WebGLExtensions=function(gl){var extensions={};this.get=function(name){if(void 0!==extensions[name])return extensions[name];var extension;switch(name){case"EXT_texture_filter_anisotropic":extension=gl.getExtension("EXT_texture_filter_anisotropic")||gl.getExtension("MOZ_EXT_texture_filter_anisotropic")||gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic");break;case"WEBGL_compressed_texture_s3tc":extension=gl.getExtension("WEBGL_compressed_texture_s3tc")||gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc")||gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");break;case"WEBGL_compressed_texture_pvrtc":extension=gl.getExtension("WEBGL_compressed_texture_pvrtc")||gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc");break;default:extension=gl.getExtension(name)}return null===extension&&console.warn("THREE.WebGLRenderer: "+name+" extension not supported."),extensions[name]=extension,extension}},THREE.WebGLCapabilities=function(gl,extensions,parameters){function getMaxPrecision(precision){if("highp"===precision){if(gl.getShaderPrecisionFormat(gl.VERTEX_SHADER,gl.HIGH_FLOAT).precision>0&&gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER,gl.HIGH_FLOAT).precision>0)return"highp";precision="mediump"}return"mediump"===precision&&gl.getShaderPrecisionFormat(gl.VERTEX_SHADER,gl.MEDIUM_FLOAT).precision>0&&gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER,gl.MEDIUM_FLOAT).precision>0?"mediump":"lowp"}this.getMaxPrecision=getMaxPrecision,this.precision=void 0!==parameters.precision?parameters.precision:"highp",this.logarithmicDepthBuffer=void 0!==parameters.logarithmicDepthBuffer?parameters.logarithmicDepthBuffer:!1,this.maxTextures=gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS),this.maxVertexTextures=gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS),this.maxTextureSize=gl.getParameter(gl.MAX_TEXTURE_SIZE),this.maxCubemapSize=gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE),this.maxAttributes=gl.getParameter(gl.MAX_VERTEX_ATTRIBS),this.maxVertexUniforms=gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS),this.maxVaryings=gl.getParameter(gl.MAX_VARYING_VECTORS),this.maxFragmentUniforms=gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS),this.vertexTextures=this.maxVertexTextures>0,this.floatFragmentTextures=!!extensions.get("OES_texture_float"),this.floatVertexTextures=this.vertexTextures&&this.floatFragmentTextures;var _maxPrecision=getMaxPrecision(this.precision);_maxPrecision!==this.precision&&(console.warn("THREE.WebGLRenderer:",this.precision,"not supported, using",_maxPrecision,"instead."),this.precision=_maxPrecision),this.logarithmicDepthBuffer&&(this.logarithmicDepthBuffer=!!extensions.get("EXT_frag_depth"))},THREE.WebGLGeometries=function(gl,properties,info){function get(object){var geometry=object.geometry;if(void 0!==geometries[geometry.id])return geometries[geometry.id];geometry.addEventListener("dispose",onGeometryDispose);var buffergeometry;return geometry instanceof THREE.BufferGeometry?buffergeometry=geometry:geometry instanceof THREE.Geometry&&(void 0===geometry._bufferGeometry&&(geometry._bufferGeometry=(new THREE.BufferGeometry).setFromObject(object)),buffergeometry=geometry._bufferGeometry),geometries[geometry.id]=buffergeometry,info.memory.geometries++,buffergeometry}function onGeometryDispose(event){var geometry=event.target,buffergeometry=geometries[geometry.id];deleteAttributes(buffergeometry.attributes),geometry.removeEventListener("dispose",onGeometryDispose),delete geometries[geometry.id];var property=properties.get(geometry);property.wireframe&&deleteAttribute(property.wireframe),info.memory.geometries--}function getAttributeBuffer(attribute){return attribute instanceof THREE.InterleavedBufferAttribute?properties.get(attribute.data).__webglBuffer:properties.get(attribute).__webglBuffer}function deleteAttribute(attribute){var buffer=getAttributeBuffer(attribute);void 0!==buffer&&(gl.deleteBuffer(buffer),removeAttributeBuffer(attribute))}function deleteAttributes(attributes){for(var name in attributes)deleteAttribute(attributes[name])}function removeAttributeBuffer(attribute){attribute instanceof THREE.InterleavedBufferAttribute?properties["delete"](attribute.data):properties["delete"](attribute)}var geometries={};this.get=get},THREE.WebGLObjects=function(gl,properties,info){function update(object){var geometry=geometries.get(object);object.geometry instanceof THREE.Geometry&&geometry.updateFromObject(object);var index=geometry.index,attributes=geometry.attributes;null!==index&&updateAttribute(index,gl.ELEMENT_ARRAY_BUFFER);for(var name in attributes)updateAttribute(attributes[name],gl.ARRAY_BUFFER);var morphAttributes=geometry.morphAttributes;for(var name in morphAttributes)for(var array=morphAttributes[name],i=0,l=array.length;l>i;i++)updateAttribute(array[i],gl.ARRAY_BUFFER);return geometry}function updateAttribute(attribute,bufferType){var data=attribute instanceof THREE.InterleavedBufferAttribute?attribute.data:attribute,attributeProperties=properties.get(data);void 0===attributeProperties.__webglBuffer?createBuffer(attributeProperties,data,bufferType):attributeProperties.version!==data.version&&updateBuffer(attributeProperties,data,bufferType)}function createBuffer(attributeProperties,data,bufferType){attributeProperties.__webglBuffer=gl.createBuffer(),gl.bindBuffer(bufferType,attributeProperties.__webglBuffer);var usage=data.dynamic?gl.DYNAMIC_DRAW:gl.STATIC_DRAW;gl.bufferData(bufferType,data.array,usage),attributeProperties.version=data.version}function updateBuffer(attributeProperties,data,bufferType){gl.bindBuffer(bufferType,attributeProperties.__webglBuffer),data.dynamic===!1||-1===data.updateRange.count?gl.bufferSubData(bufferType,0,data.array):0===data.updateRange.count?console.error("THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually."):(gl.bufferSubData(bufferType,data.updateRange.offset*data.array.BYTES_PER_ELEMENT,data.array.subarray(data.updateRange.offset,data.updateRange.offset+data.updateRange.count)),data.updateRange.count=0),attributeProperties.version=data.version}function getAttributeBuffer(attribute){return attribute instanceof THREE.InterleavedBufferAttribute?properties.get(attribute.data).__webglBuffer:properties.get(attribute).__webglBuffer}function getWireframeAttribute(geometry){var property=properties.get(geometry);if(void 0!==property.wireframe)return property.wireframe;var indices=[],index=geometry.index,attributes=geometry.attributes,position=attributes.position;if(null!==index)for(var edges={},array=index.array,i=0,l=array.length;l>i;i+=3){var a=array[i+0],b=array[i+1],c=array[i+2];checkEdge(edges,a,b)&&indices.push(a,b),checkEdge(edges,b,c)&&indices.push(b,c),checkEdge(edges,c,a)&&indices.push(c,a)}else for(var array=attributes.position.array,i=0,l=array.length/3-1;l>i;i+=3){var a=i+0,b=i+1,c=i+2;indices.push(a,b,b,c,c,a)}var TypeArray=position.count>65535?Uint32Array:Uint16Array,attribute=new THREE.BufferAttribute(new TypeArray(indices),1);return updateAttribute(attribute,gl.ELEMENT_ARRAY_BUFFER),property.wireframe=attribute,attribute}function checkEdge(edges,a,b){if(a>b){var tmp=a;a=b,b=tmp}var list=edges[a];return void 0===list?(edges[a]=[b],!0):-1===list.indexOf(b)?(list.push(b),!0):!1}var geometries=new THREE.WebGLGeometries(gl,properties,info);this.getAttributeBuffer=getAttributeBuffer,this.getWireframeAttribute=getWireframeAttribute,this.update=update},THREE.WebGLProgram=function(){function generateDefines(defines){var chunks=[];for(var name in defines){var value=defines[name];value!==!1&&chunks.push("#define "+name+" "+value)}return chunks.join("\n")}function fetchUniformLocations(gl,program,identifiers){for(var uniforms={},n=gl.getProgramParameter(program,gl.ACTIVE_UNIFORMS),i=0;n>i;i++){var info=gl.getActiveUniform(program,i),name=info.name,location=gl.getUniformLocation(program,name),suffixPos=name.lastIndexOf("[0]");-1!==suffixPos&&suffixPos===name.length-3&&(uniforms[name.substr(0,suffixPos)]=location),uniforms[name]=location}return uniforms}function fetchAttributeLocations(gl,program,identifiers){for(var attributes={},n=gl.getProgramParameter(program,gl.ACTIVE_ATTRIBUTES),i=0;n>i;i++){var info=gl.getActiveAttrib(program,i),name=info.name;attributes[name]=gl.getAttribLocation(program,name)}return attributes}function filterEmptyLine(string){return""!==string}var programIdCount=0;return function(renderer,code,material,parameters){var gl=renderer.context,defines=material.defines,vertexShader=material.__webglShader.vertexShader,fragmentShader=material.__webglShader.fragmentShader,shadowMapTypeDefine="SHADOWMAP_TYPE_BASIC";parameters.shadowMapType===THREE.PCFShadowMap?shadowMapTypeDefine="SHADOWMAP_TYPE_PCF":parameters.shadowMapType===THREE.PCFSoftShadowMap&&(shadowMapTypeDefine="SHADOWMAP_TYPE_PCF_SOFT");var envMapTypeDefine="ENVMAP_TYPE_CUBE",envMapModeDefine="ENVMAP_MODE_REFLECTION",envMapBlendingDefine="ENVMAP_BLENDING_MULTIPLY";if(parameters.envMap){switch(material.envMap.mapping){case THREE.CubeReflectionMapping:case THREE.CubeRefractionMapping:envMapTypeDefine="ENVMAP_TYPE_CUBE";break;case THREE.EquirectangularReflectionMapping:case THREE.EquirectangularRefractionMapping:envMapTypeDefine="ENVMAP_TYPE_EQUIREC";break;case THREE.SphericalReflectionMapping:envMapTypeDefine="ENVMAP_TYPE_SPHERE"}switch(material.envMap.mapping){case THREE.CubeRefractionMapping:case THREE.EquirectangularRefractionMapping:envMapModeDefine="ENVMAP_MODE_REFRACTION"}switch(material.combine){case THREE.MultiplyOperation:envMapBlendingDefine="ENVMAP_BLENDING_MULTIPLY";break;case THREE.MixOperation:envMapBlendingDefine="ENVMAP_BLENDING_MIX";break;case THREE.AddOperation:envMapBlendingDefine="ENVMAP_BLENDING_ADD"}}var prefixVertex,prefixFragment,gammaFactorDefine=renderer.gammaFactor>0?renderer.gammaFactor:1,customDefines=generateDefines(defines),program=gl.createProgram();material instanceof THREE.RawShaderMaterial?(prefixVertex="",prefixFragment=""):(prefixVertex=["precision "+parameters.precision+" float;","precision "+parameters.precision+" int;","#define SHADER_NAME "+material.__webglShader.name,customDefines,parameters.supportsVertexTextures?"#define VERTEX_TEXTURES":"",renderer.gammaInput?"#define GAMMA_INPUT":"",renderer.gammaOutput?"#define GAMMA_OUTPUT":"","#define GAMMA_FACTOR "+gammaFactorDefine,"#define MAX_DIR_LIGHTS "+parameters.maxDirLights,"#define MAX_POINT_LIGHTS "+parameters.maxPointLights,"#define MAX_SPOT_LIGHTS "+parameters.maxSpotLights,"#define MAX_HEMI_LIGHTS "+parameters.maxHemiLights,"#define MAX_SHADOWS "+parameters.maxShadows,"#define MAX_BONES "+parameters.maxBones,parameters.map?"#define USE_MAP":"",parameters.envMap?"#define USE_ENVMAP":"",parameters.envMap?"#define "+envMapModeDefine:"",parameters.lightMap?"#define USE_LIGHTMAP":"",parameters.aoMap?"#define USE_AOMAP":"",parameters.emissiveMap?"#define USE_EMISSIVEMAP":"",parameters.bumpMap?"#define USE_BUMPMAP":"",parameters.normalMap?"#define USE_NORMALMAP":"",parameters.displacementMap&¶meters.supportsVertexTextures?"#define USE_DISPLACEMENTMAP":"",parameters.specularMap?"#define USE_SPECULARMAP":"",parameters.alphaMap?"#define USE_ALPHAMAP":"",parameters.vertexColors?"#define USE_COLOR":"",parameters.flatShading?"#define FLAT_SHADED":"",parameters.skinning?"#define USE_SKINNING":"",parameters.useVertexTexture?"#define BONE_TEXTURE":"",parameters.morphTargets?"#define USE_MORPHTARGETS":"",parameters.morphNormals&¶meters.flatShading===!1?"#define USE_MORPHNORMALS":"",parameters.doubleSided?"#define DOUBLE_SIDED":"",parameters.flipSided?"#define FLIP_SIDED":"",parameters.shadowMapEnabled?"#define USE_SHADOWMAP":"",parameters.shadowMapEnabled?"#define "+shadowMapTypeDefine:"",parameters.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",parameters.pointLightShadows>0?"#define POINT_LIGHT_SHADOWS":"",parameters.sizeAttenuation?"#define USE_SIZEATTENUATION":"",parameters.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",parameters.logarithmicDepthBuffer&&renderer.extensions.get("EXT_frag_depth")?"#define USE_LOGDEPTHBUF_EXT":"","uniform mat4 modelMatrix;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform mat4 viewMatrix;","uniform mat3 normalMatrix;","uniform vec3 cameraPosition;","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_COLOR"," attribute vec3 color;","#endif","#ifdef USE_MORPHTARGETS"," attribute vec3 morphTarget0;"," attribute vec3 morphTarget1;"," attribute vec3 morphTarget2;"," attribute vec3 morphTarget3;"," #ifdef USE_MORPHNORMALS"," attribute vec3 morphNormal0;"," attribute vec3 morphNormal1;"," attribute vec3 morphNormal2;"," attribute vec3 morphNormal3;"," #else"," attribute vec3 morphTarget4;"," attribute vec3 morphTarget5;"," attribute vec3 morphTarget6;"," attribute vec3 morphTarget7;"," #endif","#endif","#ifdef USE_SKINNING"," attribute vec4 skinIndex;"," attribute vec4 skinWeight;","#endif","\n"].filter(filterEmptyLine).join("\n"),
+prefixFragment=[parameters.bumpMap||parameters.normalMap||parameters.flatShading||material.derivatives?"#extension GL_OES_standard_derivatives : enable":"",parameters.logarithmicDepthBuffer&&renderer.extensions.get("EXT_frag_depth")?"#extension GL_EXT_frag_depth : enable":"","precision "+parameters.precision+" float;","precision "+parameters.precision+" int;","#define SHADER_NAME "+material.__webglShader.name,customDefines,"#define MAX_DIR_LIGHTS "+parameters.maxDirLights,"#define MAX_POINT_LIGHTS "+parameters.maxPointLights,"#define MAX_SPOT_LIGHTS "+parameters.maxSpotLights,"#define MAX_HEMI_LIGHTS "+parameters.maxHemiLights,"#define MAX_SHADOWS "+parameters.maxShadows,parameters.alphaTest?"#define ALPHATEST "+parameters.alphaTest:"",renderer.gammaInput?"#define GAMMA_INPUT":"",renderer.gammaOutput?"#define GAMMA_OUTPUT":"","#define GAMMA_FACTOR "+gammaFactorDefine,parameters.useFog&¶meters.fog?"#define USE_FOG":"",parameters.useFog&¶meters.fogExp?"#define FOG_EXP2":"",parameters.map?"#define USE_MAP":"",parameters.envMap?"#define USE_ENVMAP":"",parameters.envMap?"#define "+envMapTypeDefine:"",parameters.envMap?"#define "+envMapModeDefine:"",parameters.envMap?"#define "+envMapBlendingDefine:"",parameters.lightMap?"#define USE_LIGHTMAP":"",parameters.aoMap?"#define USE_AOMAP":"",parameters.emissiveMap?"#define USE_EMISSIVEMAP":"",parameters.bumpMap?"#define USE_BUMPMAP":"",parameters.normalMap?"#define USE_NORMALMAP":"",parameters.specularMap?"#define USE_SPECULARMAP":"",parameters.alphaMap?"#define USE_ALPHAMAP":"",parameters.vertexColors?"#define USE_COLOR":"",parameters.flatShading?"#define FLAT_SHADED":"",parameters.metal?"#define METAL":"",parameters.doubleSided?"#define DOUBLE_SIDED":"",parameters.flipSided?"#define FLIP_SIDED":"",parameters.shadowMapEnabled?"#define USE_SHADOWMAP":"",parameters.shadowMapEnabled?"#define "+shadowMapTypeDefine:"",parameters.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",parameters.pointLightShadows>0?"#define POINT_LIGHT_SHADOWS":"",parameters.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",parameters.logarithmicDepthBuffer&&renderer.extensions.get("EXT_frag_depth")?"#define USE_LOGDEPTHBUF_EXT":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","\n"].filter(filterEmptyLine).join("\n"));var vertexGlsl=prefixVertex+vertexShader,fragmentGlsl=prefixFragment+fragmentShader,glVertexShader=THREE.WebGLShader(gl,gl.VERTEX_SHADER,vertexGlsl),glFragmentShader=THREE.WebGLShader(gl,gl.FRAGMENT_SHADER,fragmentGlsl);gl.attachShader(program,glVertexShader),gl.attachShader(program,glFragmentShader),void 0!==material.index0AttributeName?gl.bindAttribLocation(program,0,material.index0AttributeName):parameters.morphTargets===!0&&gl.bindAttribLocation(program,0,"position"),gl.linkProgram(program);var programLog=gl.getProgramInfoLog(program),vertexLog=gl.getShaderInfoLog(glVertexShader),fragmentLog=gl.getShaderInfoLog(glFragmentShader),runnable=!0,haveDiagnostics=!0;gl.getProgramParameter(program,gl.LINK_STATUS)===!1?(runnable=!1,console.error("THREE.WebGLProgram: shader error: ",gl.getError(),"gl.VALIDATE_STATUS",gl.getProgramParameter(program,gl.VALIDATE_STATUS),"gl.getProgramInfoLog",programLog,vertexLog,fragmentLog)):""!==programLog?console.warn("THREE.WebGLProgram: gl.getProgramInfoLog()",programLog):""!==vertexLog&&""!==fragmentLog||(haveDiagnostics=!1),haveDiagnostics&&(this.diagnostics={runnable:runnable,material:material,programLog:programLog,vertexShader:{log:vertexLog,prefix:prefixVertex},fragmentShader:{log:fragmentLog,prefix:prefixFragment}}),gl.deleteShader(glVertexShader),gl.deleteShader(glFragmentShader);var cachedUniforms;this.getUniforms=function(){return void 0===cachedUniforms&&(cachedUniforms=fetchUniformLocations(gl,program)),cachedUniforms};var cachedAttributes;return this.getAttributes=function(){return void 0===cachedAttributes&&(cachedAttributes=fetchAttributeLocations(gl,program)),cachedAttributes},this.destroy=function(){gl.deleteProgram(program),this.program=void 0},Object.defineProperties(this,{uniforms:{get:function(){return console.warn("THREE.WebGLProgram: .uniforms is now .getUniforms()."),this.getUniforms()}},attributes:{get:function(){return console.warn("THREE.WebGLProgram: .attributes is now .getAttributes()."),this.getAttributes()}}}),this.id=programIdCount++,this.code=code,this.usedTimes=1,this.program=program,this.vertexShader=glVertexShader,this.fragmentShader=glFragmentShader,this}}(),THREE.WebGLPrograms=function(renderer,capabilities){function allocateBones(object){if(capabilities.floatVertexTextures&&object&&object.skeleton&&object.skeleton.useVertexTexture)return 1024;var nVertexUniforms=capabilities.maxVertexUniforms,nVertexMatrices=Math.floor((nVertexUniforms-20)/4),maxBones=nVertexMatrices;return void 0!==object&&object instanceof THREE.SkinnedMesh&&(maxBones=Math.min(object.skeleton.bones.length,maxBones),maxBonesl;l++){var light=lights[l];light.visible!==!1&&(light instanceof THREE.DirectionalLight&&dirLights++,light instanceof THREE.PointLight&&pointLights++,light instanceof THREE.SpotLight&&spotLights++,light instanceof THREE.HemisphereLight&&hemiLights++)}return{directional:dirLights,point:pointLights,spot:spotLights,hemi:hemiLights}}function allocateShadows(lights){for(var maxShadows=0,pointLightShadows=0,l=0,ll=lights.length;ll>l;l++){var light=lights[l];light.castShadow&&((light instanceof THREE.SpotLight||light instanceof THREE.DirectionalLight)&&maxShadows++,light instanceof THREE.PointLight&&(maxShadows++,pointLightShadows++))}return{maxShadows:maxShadows,pointLightShadows:pointLightShadows}}var programs=[],shaderIDs={MeshDepthMaterial:"depth",MeshNormalMaterial:"normal",MeshBasicMaterial:"basic",MeshLambertMaterial:"lambert",MeshPhongMaterial:"phong",LineBasicMaterial:"basic",LineDashedMaterial:"dashed",PointsMaterial:"points"},parameterNames=["precision","supportsVertexTextures","map","envMap","envMapMode","lightMap","aoMap","emissiveMap","bumpMap","normalMap","displacementMap","specularMap","alphaMap","combine","vertexColors","fog","useFog","fogExp","flatShading","sizeAttenuation","logarithmicDepthBuffer","skinning","maxBones","useVertexTexture","morphTargets","morphNormals","maxMorphTargets","maxMorphNormals","maxDirLights","maxPointLights","maxSpotLights","maxHemiLights","maxShadows","shadowMapEnabled","pointLightShadows","shadowMapType","shadowMapDebug","alphaTest","metal","doubleSided","flipSided"];this.getParameters=function(material,lights,fog,object){var shaderID=shaderIDs[material.type],maxLightCount=allocateLights(lights),allocatedShadows=allocateShadows(lights),maxBones=allocateBones(object),precision=renderer.getPrecision();null!==material.precision&&(precision=capabilities.getMaxPrecision(material.precision),precision!==material.precision&&console.warn("THREE.WebGLRenderer.initMaterial:",material.precision,"not supported, using",precision,"instead."));var parameters={shaderID:shaderID,precision:precision,supportsVertexTextures:capabilities.vertexTextures,map:!!material.map,envMap:!!material.envMap,envMapMode:material.envMap&&material.envMap.mapping,lightMap:!!material.lightMap,aoMap:!!material.aoMap,emissiveMap:!!material.emissiveMap,bumpMap:!!material.bumpMap,normalMap:!!material.normalMap,displacementMap:!!material.displacementMap,specularMap:!!material.specularMap,alphaMap:!!material.alphaMap,combine:material.combine,vertexColors:material.vertexColors,fog:fog,useFog:material.fog,fogExp:fog instanceof THREE.FogExp2,flatShading:material.shading===THREE.FlatShading,sizeAttenuation:material.sizeAttenuation,logarithmicDepthBuffer:capabilities.logarithmicDepthBuffer,skinning:material.skinning,maxBones:maxBones,useVertexTexture:capabilities.floatVertexTextures&&object&&object.skeleton&&object.skeleton.useVertexTexture,morphTargets:material.morphTargets,morphNormals:material.morphNormals,maxMorphTargets:renderer.maxMorphTargets,maxMorphNormals:renderer.maxMorphNormals,maxDirLights:maxLightCount.directional,maxPointLights:maxLightCount.point,maxSpotLights:maxLightCount.spot,maxHemiLights:maxLightCount.hemi,maxShadows:allocatedShadows.maxShadows,pointLightShadows:allocatedShadows.pointLightShadows,shadowMapEnabled:renderer.shadowMap.enabled&&object.receiveShadow&&allocatedShadows.maxShadows>0,shadowMapType:renderer.shadowMap.type,shadowMapDebug:renderer.shadowMap.debug,alphaTest:material.alphaTest,metal:material.metal,doubleSided:material.side===THREE.DoubleSide,flipSided:material.side===THREE.BackSide};return parameters},this.getProgramCode=function(material,parameters){var chunks=[];if(parameters.shaderID?chunks.push(parameters.shaderID):(chunks.push(material.fragmentShader),chunks.push(material.vertexShader)),void 0!==material.defines)for(var name in material.defines)chunks.push(name),chunks.push(material.defines[name]);for(var i=0;ip;p++){var programInfo=programs[p];if(programInfo.code===code){program=programInfo,++program.usedTimes;break}}return void 0===program&&(program=new THREE.WebGLProgram(renderer,code,material,parameters),programs.push(program)),program},this.releaseProgram=function(program){if(0===--program.usedTimes){var i=programs.indexOf(program);programs[i]=programs[programs.length-1],programs.pop(),program.destroy()}},this.programs=programs},THREE.WebGLProperties=function(){var properties={};this.get=function(object){var uuid=object.uuid,map=properties[uuid];return void 0===map&&(map={},properties[uuid]=map),map},this["delete"]=function(object){delete properties[object.uuid]},this.clear=function(){properties={}}},THREE.WebGLShader=function(){function addLineNumbers(string){for(var lines=string.split("\n"),i=0;i0&&material.morphTargets,useSkinning=object instanceof THREE.SkinnedMesh&&material.skinning,variantIndex=0;useMorphing&&(variantIndex|=_MorphingFlag),useSkinning&&(variantIndex|=_SkinningFlag),newMaterial=materialVariants[variantIndex]}return newMaterial.visible=material.visible,newMaterial.wireframe=material.wireframe,newMaterial.wireframeLinewidth=material.wireframeLinewidth,isPointLight&&void 0!==newMaterial.uniforms.lightPos&&newMaterial.uniforms.lightPos.value.copy(lightPositionWorld),newMaterial}function projectObject(object,camera){if(object.visible!==!1){if((object instanceof THREE.Mesh||object instanceof THREE.Line||object instanceof THREE.Points)&&object.castShadow&&(object.frustumCulled===!1||_frustum.intersectsObject(object)===!0)){var material=object.material;material.visible===!0&&(object.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse,object.matrixWorld),_renderList.push(object))}for(var children=object.children,i=0,l=children.length;l>i;i++)projectObject(children[i],camera)}}for(var _gl=_renderer.context,_state=_renderer.state,_frustum=new THREE.Frustum,_projScreenMatrix=new THREE.Matrix4,_lookTarget=(new THREE.Vector3,new THREE.Vector3,new THREE.Vector3),_lightPositionWorld=new THREE.Vector3,_renderList=[],_MorphingFlag=1,_SkinningFlag=2,_NumberOfMaterialVariants=(_MorphingFlag|_SkinningFlag)+1,_depthMaterials=new Array(_NumberOfMaterialVariants),_distanceMaterials=new Array(_NumberOfMaterialVariants),cubeDirections=[new THREE.Vector3(1,0,0),new THREE.Vector3(-1,0,0),new THREE.Vector3(0,0,1),new THREE.Vector3(0,0,-1),new THREE.Vector3(0,1,0),new THREE.Vector3(0,-1,0)],cubeUps=[new THREE.Vector3(0,1,0),new THREE.Vector3(0,1,0),new THREE.Vector3(0,1,0),new THREE.Vector3(0,1,0),new THREE.Vector3(0,0,1),new THREE.Vector3(0,0,-1)],cube2DViewPorts=[new THREE.Vector4,new THREE.Vector4,new THREE.Vector4,new THREE.Vector4,new THREE.Vector4,new THREE.Vector4],_vector4=new THREE.Vector4,depthShader=THREE.ShaderLib.depthRGBA,depthUniforms=THREE.UniformsUtils.clone(depthShader.uniforms),distanceShader=THREE.ShaderLib.distanceRGBA,distanceUniforms=THREE.UniformsUtils.clone(distanceShader.uniforms),i=0;i!==_NumberOfMaterialVariants;++i){var useMorphing=0!==(i&_MorphingFlag),useSkinning=0!==(i&_SkinningFlag),depthMaterial=new THREE.ShaderMaterial({uniforms:depthUniforms,vertexShader:depthShader.vertexShader,fragmentShader:depthShader.fragmentShader,morphTargets:useMorphing,skinning:useSkinning});depthMaterial._shadowPass=!0,_depthMaterials[i]=depthMaterial;var distanceMaterial=new THREE.ShaderMaterial({uniforms:distanceUniforms,vertexShader:distanceShader.vertexShader,fragmentShader:distanceShader.fragmentShader,morphTargets:useMorphing,skinning:useSkinning});distanceMaterial._shadowPass=!0,_distanceMaterials[i]=distanceMaterial}var scope=this;this.enabled=!1,this.autoUpdate=!0,this.needsUpdate=!1,this.type=THREE.PCFShadowMap,this.cullFace=THREE.CullFaceFront,this.render=function(scene){var faceCount,isPointLight;if(scope.enabled!==!1&&(scope.autoUpdate!==!1||scope.needsUpdate!==!1)){_gl.clearColor(1,1,1,1),_state.disable(_gl.BLEND),_state.enable(_gl.CULL_FACE),_gl.frontFace(_gl.CCW),_gl.cullFace(scope.cullFace===THREE.CullFaceFront?_gl.FRONT:_gl.BACK),_state.setDepthTest(!0),_renderer.getViewport(_vector4);for(var i=0,il=_lights.length;il>i;i++){var light=_lights[i];if(light.castShadow===!0){var shadow=light.shadow,shadowCamera=shadow.camera,shadowMapSize=shadow.mapSize;if(light instanceof THREE.PointLight){faceCount=6,isPointLight=!0;var vpWidth=shadowMapSize.x/4,vpHeight=shadowMapSize.y/2;cube2DViewPorts[0].set(2*vpWidth,vpHeight,vpWidth,vpHeight),cube2DViewPorts[1].set(0,vpHeight,vpWidth,vpHeight),cube2DViewPorts[2].set(3*vpWidth,vpHeight,vpWidth,vpHeight),cube2DViewPorts[3].set(vpWidth,vpHeight,vpWidth,vpHeight),cube2DViewPorts[4].set(3*vpWidth,0,vpWidth,vpHeight),cube2DViewPorts[5].set(vpWidth,0,vpWidth,vpHeight)}else faceCount=1,isPointLight=!1;if(null===shadow.map){var shadowFilter=THREE.LinearFilter;scope.type===THREE.PCFSoftShadowMap&&(shadowFilter=THREE.NearestFilter);var pars={minFilter:shadowFilter,magFilter:shadowFilter,format:THREE.RGBAFormat};shadow.map=new THREE.WebGLRenderTarget(shadowMapSize.x,shadowMapSize.y,pars),shadow.matrix=new THREE.Matrix4,light instanceof THREE.SpotLight&&(shadowCamera.aspect=shadowMapSize.x/shadowMapSize.y),shadowCamera.updateProjectionMatrix()}var shadowMap=shadow.map,shadowMatrix=shadow.matrix;_lightPositionWorld.setFromMatrixPosition(light.matrixWorld),shadowCamera.position.copy(_lightPositionWorld),_renderer.setRenderTarget(shadowMap),_renderer.clear();for(var face=0;faceCount>face;face++){if(isPointLight){_lookTarget.copy(shadowCamera.position),_lookTarget.add(cubeDirections[face]),shadowCamera.up.copy(cubeUps[face]),shadowCamera.lookAt(_lookTarget);var vpDimensions=cube2DViewPorts[face];_renderer.setViewport(vpDimensions.x,vpDimensions.y,vpDimensions.z,vpDimensions.w)}else _lookTarget.setFromMatrixPosition(light.target.matrixWorld),shadowCamera.lookAt(_lookTarget);shadowCamera.updateMatrixWorld(),shadowCamera.matrixWorldInverse.getInverse(shadowCamera.matrixWorld),shadowMatrix.set(.5,0,0,.5,0,.5,0,.5,0,0,.5,.5,0,0,0,1),shadowMatrix.multiply(shadowCamera.projectionMatrix),shadowMatrix.multiply(shadowCamera.matrixWorldInverse),_projScreenMatrix.multiplyMatrices(shadowCamera.projectionMatrix,shadowCamera.matrixWorldInverse),_frustum.setFromMatrix(_projScreenMatrix),_renderList.length=0,projectObject(scene,shadowCamera);for(var j=0,jl=_renderList.length;jl>j;j++){var object=_renderList[j],geometry=_objects.update(object),material=object.material;if(material instanceof THREE.MeshFaceMaterial)for(var groups=geometry.groups,materials=material.materials,k=0,kl=groups.length;kl>k;k++){var group=groups[k],groupMaterial=materials[group.materialIndex];if(groupMaterial.visible===!0){var depthMaterial=getDepthMaterial(object,groupMaterial,isPointLight,_lightPositionWorld);_renderer.renderBufferDirect(shadowCamera,_lights,null,geometry,depthMaterial,object,group)}}else{var depthMaterial=getDepthMaterial(object,material,isPointLight,_lightPositionWorld);_renderer.renderBufferDirect(shadowCamera,_lights,null,geometry,depthMaterial,object,null)}}}_renderer.resetGLState()}}_renderer.setViewport(_vector4.x,_vector4.y,_vector4.z,_vector4.w);var clearColor=_renderer.getClearColor(),clearAlpha=_renderer.getClearAlpha();_renderer.setClearColor(clearColor,clearAlpha),_state.enable(_gl.BLEND),scope.cullFace===THREE.CullFaceFront&&_gl.cullFace(_gl.BACK),_renderer.resetGLState(),scope.needsUpdate=!1}}},THREE.WebGLState=function(gl,extensions,paramThreeToGL){var _this=this,newAttributes=new Uint8Array(16),enabledAttributes=new Uint8Array(16),attributeDivisors=new Uint8Array(16),capabilities={},compressedTextureFormats=null,currentBlending=null,currentBlendEquation=null,currentBlendSrc=null,currentBlendDst=null,currentBlendEquationAlpha=null,currentBlendSrcAlpha=null,currentBlendDstAlpha=null,currentDepthFunc=null,currentDepthWrite=null,currentColorWrite=null,currentFlipSided=null,currentLineWidth=null,currentPolygonOffsetFactor=null,currentPolygonOffsetUnits=null,maxTextures=gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS),currentTextureSlot=void 0,currentBoundTextures={};this.init=function(){gl.clearColor(0,0,0,1),gl.clearDepth(1),gl.clearStencil(0),this.enable(gl.DEPTH_TEST),gl.depthFunc(gl.LEQUAL),gl.frontFace(gl.CCW),gl.cullFace(gl.BACK),this.enable(gl.CULL_FACE),this.enable(gl.BLEND),gl.blendEquation(gl.FUNC_ADD),gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA)},this.initAttributes=function(){for(var i=0,l=newAttributes.length;l>i;i++)newAttributes[i]=0},this.enableAttribute=function(attribute){if(newAttributes[attribute]=1,0===enabledAttributes[attribute]&&(gl.enableVertexAttribArray(attribute),enabledAttributes[attribute]=1),0!==attributeDivisors[attribute]){var extension=extensions.get("ANGLE_instanced_arrays");extension.vertexAttribDivisorANGLE(attribute,0),attributeDivisors[attribute]=0}},this.enableAttributeAndDivisor=function(attribute,meshPerAttribute,extension){newAttributes[attribute]=1,0===enabledAttributes[attribute]&&(gl.enableVertexAttribArray(attribute),enabledAttributes[attribute]=1),attributeDivisors[attribute]!==meshPerAttribute&&(extension.vertexAttribDivisorANGLE(attribute,meshPerAttribute),attributeDivisors[attribute]=meshPerAttribute)},this.disableUnusedAttributes=function(){for(var i=0,l=enabledAttributes.length;l>i;i++)enabledAttributes[i]!==newAttributes[i]&&(gl.disableVertexAttribArray(i),enabledAttributes[i]=0)},this.enable=function(id){capabilities[id]!==!0&&(gl.enable(id),capabilities[id]=!0)},this.disable=function(id){capabilities[id]!==!1&&(gl.disable(id),capabilities[id]=!1)},this.getCompressedTextureFormats=function(){if(null===compressedTextureFormats&&(compressedTextureFormats=[],extensions.get("WEBGL_compressed_texture_pvrtc")||extensions.get("WEBGL_compressed_texture_s3tc")))for(var formats=gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS),i=0;i0;var shader;shader=hasVertexTexture?{vertexShader:["uniform lowp int renderType;","uniform vec3 screenPosition;","uniform vec2 scale;","uniform float rotation;","uniform sampler2D occlusionMap;","attribute vec2 position;","attribute vec2 uv;","varying vec2 vUV;","varying float vVisibility;","void main() {","vUV = uv;","vec2 pos = position;","if ( renderType == 2 ) {","vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );","visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );","visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );","visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );","visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );","visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );","visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );","visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );","visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );","vVisibility = visibility.r / 9.0;","vVisibility *= 1.0 - visibility.g / 9.0;","vVisibility *= visibility.b / 9.0;","vVisibility *= 1.0 - visibility.a / 9.0;","pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;","pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;","}","gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );","}"].join("\n"),fragmentShader:["uniform lowp int renderType;","uniform sampler2D map;","uniform float opacity;","uniform vec3 color;","varying vec2 vUV;","varying float vVisibility;","void main() {","if ( renderType == 0 ) {","gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );","} else if ( renderType == 1 ) {","gl_FragColor = texture2D( map, vUV );","} else {","vec4 texture = texture2D( map, vUV );","texture.a *= opacity * vVisibility;","gl_FragColor = texture;","gl_FragColor.rgb *= color;","}","}"].join("\n")}:{vertexShader:["uniform lowp int renderType;","uniform vec3 screenPosition;","uniform vec2 scale;","uniform float rotation;","attribute vec2 position;","attribute vec2 uv;","varying vec2 vUV;","void main() {","vUV = uv;","vec2 pos = position;","if ( renderType == 2 ) {","pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;","pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;","}","gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );","}"].join("\n"),fragmentShader:["precision mediump float;","uniform lowp int renderType;","uniform sampler2D map;","uniform sampler2D occlusionMap;","uniform float opacity;","uniform vec3 color;","varying vec2 vUV;","void main() {","if ( renderType == 0 ) {","gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );","} else if ( renderType == 1 ) {","gl_FragColor = texture2D( map, vUV );","} else {","float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;","visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;","visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;","visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;","visibility = ( 1.0 - visibility / 4.0 );","vec4 texture = texture2D( map, vUV );","texture.a *= opacity * visibility;","gl_FragColor = texture;","gl_FragColor.rgb *= color;","}","}"].join("\n")},program=createProgram(shader),attributes={vertex:gl.getAttribLocation(program,"position"),uv:gl.getAttribLocation(program,"uv")},uniforms={renderType:gl.getUniformLocation(program,"renderType"),map:gl.getUniformLocation(program,"map"),occlusionMap:gl.getUniformLocation(program,"occlusionMap"),opacity:gl.getUniformLocation(program,"opacity"),color:gl.getUniformLocation(program,"color"),scale:gl.getUniformLocation(program,"scale"),rotation:gl.getUniformLocation(program,"rotation"),screenPosition:gl.getUniformLocation(program,"screenPosition")}}function createProgram(shader){var program=gl.createProgram(),fragmentShader=gl.createShader(gl.FRAGMENT_SHADER),vertexShader=gl.createShader(gl.VERTEX_SHADER),prefix="precision "+renderer.getPrecision()+" float;\n";return gl.shaderSource(fragmentShader,prefix+shader.fragmentShader),gl.shaderSource(vertexShader,prefix+shader.vertexShader),gl.compileShader(fragmentShader),gl.compileShader(vertexShader),gl.attachShader(program,fragmentShader),gl.attachShader(program,vertexShader),gl.linkProgram(program),program}var vertexBuffer,elementBuffer,program,attributes,uniforms,hasVertexTexture,tempTexture,occlusionTexture,gl=renderer.context,state=renderer.state;this.render=function(scene,camera,viewportWidth,viewportHeight){if(0!==flares.length){var tempPosition=new THREE.Vector3,invAspect=viewportHeight/viewportWidth,halfViewportWidth=.5*viewportWidth,halfViewportHeight=.5*viewportHeight,size=16/viewportHeight,scale=new THREE.Vector2(size*invAspect,size),screenPosition=new THREE.Vector3(1,1,0),screenPositionPixels=new THREE.Vector2(1,1);void 0===program&&init(),gl.useProgram(program),state.initAttributes(),state.enableAttribute(attributes.vertex),state.enableAttribute(attributes.uv),state.disableUnusedAttributes(),gl.uniform1i(uniforms.occlusionMap,0),gl.uniform1i(uniforms.map,1),gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer),gl.vertexAttribPointer(attributes.vertex,2,gl.FLOAT,!1,16,0),gl.vertexAttribPointer(attributes.uv,2,gl.FLOAT,!1,16,8),gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,elementBuffer),state.disable(gl.CULL_FACE),gl.depthMask(!1);for(var i=0,l=flares.length;l>i;i++){size=16/viewportHeight,scale.set(size*invAspect,size);var flare=flares[i];if(tempPosition.set(flare.matrixWorld.elements[12],flare.matrixWorld.elements[13],flare.matrixWorld.elements[14]),tempPosition.applyMatrix4(camera.matrixWorldInverse),tempPosition.applyProjection(camera.projectionMatrix),screenPosition.copy(tempPosition),screenPositionPixels.x=screenPosition.x*halfViewportWidth+halfViewportWidth,screenPositionPixels.y=screenPosition.y*halfViewportHeight+halfViewportHeight,
+hasVertexTexture||screenPositionPixels.x>0&&screenPositionPixels.x0&&screenPositionPixels.yj;j++){var sprite=flare.lensFlares[j];sprite.opacity>.001&&sprite.scale>.001&&(screenPosition.x=sprite.x,screenPosition.y=sprite.y,screenPosition.z=sprite.z,size=sprite.size*sprite.scale/viewportHeight,scale.x=size*invAspect,scale.y=size,gl.uniform3f(uniforms.screenPosition,screenPosition.x,screenPosition.y,screenPosition.z),gl.uniform2f(uniforms.scale,scale.x,scale.y),gl.uniform1f(uniforms.rotation,sprite.rotation),gl.uniform1f(uniforms.opacity,sprite.opacity),gl.uniform3f(uniforms.color,sprite.color.r,sprite.color.g,sprite.color.b),state.setBlending(sprite.blending,sprite.blendEquation,sprite.blendSrc,sprite.blendDst),renderer.setTexture(sprite.texture,1),gl.drawElements(gl.TRIANGLES,6,gl.UNSIGNED_SHORT,0))}}}state.enable(gl.CULL_FACE),state.enable(gl.DEPTH_TEST),gl.depthMask(!0),renderer.resetGLState()}}},THREE.SpritePlugin=function(renderer,sprites){function init(){var vertices=new Float32Array([-.5,-.5,0,0,.5,-.5,1,0,.5,.5,1,1,-.5,.5,0,1]),faces=new Uint16Array([0,1,2,0,2,3]);vertexBuffer=gl.createBuffer(),elementBuffer=gl.createBuffer(),gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer),gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW),gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,elementBuffer),gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,faces,gl.STATIC_DRAW),program=createProgram(),attributes={position:gl.getAttribLocation(program,"position"),uv:gl.getAttribLocation(program,"uv")},uniforms={uvOffset:gl.getUniformLocation(program,"uvOffset"),uvScale:gl.getUniformLocation(program,"uvScale"),rotation:gl.getUniformLocation(program,"rotation"),scale:gl.getUniformLocation(program,"scale"),color:gl.getUniformLocation(program,"color"),map:gl.getUniformLocation(program,"map"),opacity:gl.getUniformLocation(program,"opacity"),modelViewMatrix:gl.getUniformLocation(program,"modelViewMatrix"),projectionMatrix:gl.getUniformLocation(program,"projectionMatrix"),fogType:gl.getUniformLocation(program,"fogType"),fogDensity:gl.getUniformLocation(program,"fogDensity"),fogNear:gl.getUniformLocation(program,"fogNear"),fogFar:gl.getUniformLocation(program,"fogFar"),fogColor:gl.getUniformLocation(program,"fogColor"),alphaTest:gl.getUniformLocation(program,"alphaTest")};var canvas=document.createElement("canvas");canvas.width=8,canvas.height=8;var context=canvas.getContext("2d");context.fillStyle="white",context.fillRect(0,0,8,8),texture=new THREE.Texture(canvas),texture.needsUpdate=!0}function createProgram(){var program=gl.createProgram(),vertexShader=gl.createShader(gl.VERTEX_SHADER),fragmentShader=gl.createShader(gl.FRAGMENT_SHADER);return gl.shaderSource(vertexShader,["precision "+renderer.getPrecision()+" float;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform float rotation;","uniform vec2 scale;","uniform vec2 uvOffset;","uniform vec2 uvScale;","attribute vec2 position;","attribute vec2 uv;","varying vec2 vUV;","void main() {","vUV = uvOffset + uv * uvScale;","vec2 alignedPosition = position * scale;","vec2 rotatedPosition;","rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;","rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;","vec4 finalPosition;","finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );","finalPosition.xy += rotatedPosition;","finalPosition = projectionMatrix * finalPosition;","gl_Position = finalPosition;","}"].join("\n")),gl.shaderSource(fragmentShader,["precision "+renderer.getPrecision()+" float;","uniform vec3 color;","uniform sampler2D map;","uniform float opacity;","uniform int fogType;","uniform vec3 fogColor;","uniform float fogDensity;","uniform float fogNear;","uniform float fogFar;","uniform float alphaTest;","varying vec2 vUV;","void main() {","vec4 texture = texture2D( map, vUV );","if ( texture.a < alphaTest ) discard;","gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );","if ( fogType > 0 ) {","float depth = gl_FragCoord.z / gl_FragCoord.w;","float fogFactor = 0.0;","if ( fogType == 1 ) {","fogFactor = smoothstep( fogNear, fogFar, depth );","} else {","const float LOG2 = 1.442695;","fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );","fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );","}","gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );","}","}"].join("\n")),gl.compileShader(vertexShader),gl.compileShader(fragmentShader),gl.attachShader(program,vertexShader),gl.attachShader(program,fragmentShader),gl.linkProgram(program),program}function painterSortStable(a,b){return a.z!==b.z?b.z-a.z:b.id-a.id}var vertexBuffer,elementBuffer,program,attributes,uniforms,texture,gl=renderer.context,state=renderer.state,spritePosition=new THREE.Vector3,spriteRotation=new THREE.Quaternion,spriteScale=new THREE.Vector3;this.render=function(scene,camera){if(0!==sprites.length){void 0===program&&init(),gl.useProgram(program),state.initAttributes(),state.enableAttribute(attributes.position),state.enableAttribute(attributes.uv),state.disableUnusedAttributes(),state.disable(gl.CULL_FACE),state.enable(gl.BLEND),gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer),gl.vertexAttribPointer(attributes.position,2,gl.FLOAT,!1,16,0),gl.vertexAttribPointer(attributes.uv,2,gl.FLOAT,!1,16,8),gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,elementBuffer),gl.uniformMatrix4fv(uniforms.projectionMatrix,!1,camera.projectionMatrix.elements),state.activeTexture(gl.TEXTURE0),gl.uniform1i(uniforms.map,0);var oldFogType=0,sceneFogType=0,fog=scene.fog;fog?(gl.uniform3f(uniforms.fogColor,fog.color.r,fog.color.g,fog.color.b),fog instanceof THREE.Fog?(gl.uniform1f(uniforms.fogNear,fog.near),gl.uniform1f(uniforms.fogFar,fog.far),gl.uniform1i(uniforms.fogType,1),oldFogType=1,sceneFogType=1):fog instanceof THREE.FogExp2&&(gl.uniform1f(uniforms.fogDensity,fog.density),gl.uniform1i(uniforms.fogType,2),oldFogType=2,sceneFogType=2)):(gl.uniform1i(uniforms.fogType,0),oldFogType=0,sceneFogType=0);for(var i=0,l=sprites.length;l>i;i++){var sprite=sprites[i];sprite.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse,sprite.matrixWorld),sprite.z=-sprite.modelViewMatrix.elements[14]}sprites.sort(painterSortStable);for(var scale=[],i=0,l=sprites.length;l>i;i++){var sprite=sprites[i],material=sprite.material;gl.uniform1f(uniforms.alphaTest,material.alphaTest),gl.uniformMatrix4fv(uniforms.modelViewMatrix,!1,sprite.modelViewMatrix.elements),sprite.matrixWorld.decompose(spritePosition,spriteRotation,spriteScale),scale[0]=spriteScale.x,scale[1]=spriteScale.y;var fogType=0;scene.fog&&material.fog&&(fogType=sceneFogType),oldFogType!==fogType&&(gl.uniform1i(uniforms.fogType,fogType),oldFogType=fogType),null!==material.map?(gl.uniform2f(uniforms.uvOffset,material.map.offset.x,material.map.offset.y),gl.uniform2f(uniforms.uvScale,material.map.repeat.x,material.map.repeat.y)):(gl.uniform2f(uniforms.uvOffset,0,0),gl.uniform2f(uniforms.uvScale,1,1)),gl.uniform1f(uniforms.opacity,material.opacity),gl.uniform3f(uniforms.color,material.color.r,material.color.g,material.color.b),gl.uniform1f(uniforms.rotation,material.rotation),gl.uniform2fv(uniforms.scale,scale),state.setBlending(material.blending,material.blendEquation,material.blendSrc,material.blendDst),state.setDepthTest(material.depthTest),state.setDepthWrite(material.depthWrite),material.map&&material.map.image&&material.map.image.width?renderer.setTexture(material.map,0):renderer.setTexture(texture,0),gl.drawElements(gl.TRIANGLES,6,gl.UNSIGNED_SHORT,0)}state.enable(gl.CULL_FACE),renderer.resetGLState()}}},THREE.CurveUtils={tangentQuadraticBezier:function(t,p0,p1,p2){return 2*(1-t)*(p1-p0)+2*t*(p2-p1)},tangentCubicBezier:function(t,p0,p1,p2,p3){return-3*p0*(1-t)*(1-t)+3*p1*(1-t)*(1-t)-6*t*p1*(1-t)+6*t*p2*(1-t)-3*t*t*p2+3*t*t*p3},tangentSpline:function(t,p0,p1,p2,p3){var h00=6*t*t-6*t,h10=3*t*t-4*t+1,h01=-6*t*t+6*t,h11=3*t*t-2*t;return h00+h10+h01+h11},interpolate:function(p0,p1,p2,p3,t){var v0=.5*(p2-p0),v1=.5*(p3-p1),t2=t*t,t3=t*t2;return(2*p1-2*p2+v0+v1)*t3+(-3*p1+3*p2-2*v0-v1)*t2+v0*t+p1}},THREE.GeometryUtils={merge:function(geometry1,geometry2,materialIndexOffset){console.warn("THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.");var matrix;geometry2 instanceof THREE.Mesh&&(geometry2.matrixAutoUpdate&&geometry2.updateMatrix(),matrix=geometry2.matrix,geometry2=geometry2.geometry),geometry1.merge(geometry2,matrix,materialIndexOffset)},center:function(geometry){return console.warn("THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead."),geometry.center()}},THREE.ImageUtils={crossOrigin:void 0,loadTexture:function(url,mapping,onLoad,onError){console.warn("THREE.ImageUtils.loadTexture is being deprecated. Use THREE.TextureLoader() instead.");var loader=new THREE.TextureLoader;loader.setCrossOrigin(this.crossOrigin);var texture=loader.load(url,onLoad,void 0,onError);return mapping&&(texture.mapping=mapping),texture},loadTextureCube:function(urls,mapping,onLoad,onError){console.warn("THREE.ImageUtils.loadTextureCube is being deprecated. Use THREE.CubeTextureLoader() instead.");var loader=new THREE.CubeTextureLoader;loader.setCrossOrigin(this.crossOrigin);var texture=loader.load(urls,onLoad,void 0,onError);return mapping&&(texture.mapping=mapping),texture},loadCompressedTexture:function(){console.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.")},loadCompressedTextureCube:function(){console.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.")}},THREE.SceneUtils={createMultiMaterialObject:function(geometry,materials){for(var group=new THREE.Group,i=0,l=materials.length;l>i;i++)group.add(new THREE.Mesh(geometry,materials[i]));return group},detach:function(child,parent,scene){child.applyMatrix(parent.matrixWorld),parent.remove(child),scene.add(child)},attach:function(child,scene,parent){var matrixWorldInverse=new THREE.Matrix4;matrixWorldInverse.getInverse(parent.matrixWorld),child.applyMatrix(matrixWorldInverse),scene.remove(child),parent.add(child)}},THREE.ShapeUtils={area:function(contour){for(var n=contour.length,a=0,p=n-1,q=0;n>q;p=q++)a+=contour[p].x*contour[q].y-contour[q].x*contour[p].y;return.5*a},triangulate:function(){function snip(contour,u,v,w,n,verts){var p,ax,ay,bx,by,cx,cy,px,py;if(ax=contour[verts[u]].x,ay=contour[verts[u]].y,bx=contour[verts[v]].x,by=contour[verts[v]].y,cx=contour[verts[w]].x,cy=contour[verts[w]].y,Number.EPSILON>(bx-ax)*(cy-ay)-(by-ay)*(cx-ax))return!1;var aX,aY,bX,bY,cX,cY,apx,apy,bpx,bpy,cpx,cpy,cCROSSap,bCROSScp,aCROSSbp;for(aX=cx-bx,aY=cy-by,bX=ax-cx,bY=ay-cy,cX=bx-ax,cY=by-ay,p=0;n>p;p++)if(px=contour[verts[p]].x,py=contour[verts[p]].y,!(px===ax&&py===ay||px===bx&&py===by||px===cx&&py===cy)&&(apx=px-ax,apy=py-ay,bpx=px-bx,bpy=py-by,cpx=px-cx,cpy=py-cy,aCROSSbp=aX*bpy-aY*bpx,cCROSSap=cX*apy-cY*apx,bCROSScp=bX*cpy-bY*cpx,aCROSSbp>=-Number.EPSILON&&bCROSScp>=-Number.EPSILON&&cCROSSap>=-Number.EPSILON))return!1;return!0}return function(contour,indices){var n=contour.length;if(3>n)return null;var u,v,w,result=[],verts=[],vertIndices=[];if(THREE.ShapeUtils.area(contour)>0)for(v=0;n>v;v++)verts[v]=v;else for(v=0;n>v;v++)verts[v]=n-1-v;var nv=n,count=2*nv;for(v=nv-1;nv>2;){if(count--<=0)return console.warn("THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()"),indices?vertIndices:result;if(u=v,u>=nv&&(u=0),v=u+1,v>=nv&&(v=0),w=v+1,w>=nv&&(w=0),snip(contour,u,v,w,nv,verts)){var a,b,c,s,t;for(a=verts[u],b=verts[v],c=verts[w],result.push([contour[a],contour[b],contour[c]]),vertIndices.push([verts[u],verts[v],verts[w]]),s=v,t=v+1;nv>t;s++,t++)verts[s]=verts[t];nv--,count=2*nv}}return indices?vertIndices:result}}(),triangulateShape:function(contour,holes){function point_in_segment_2D_colin(inSegPt1,inSegPt2,inOtherPt){return inSegPt1.x!==inSegPt2.x?inSegPt1.xNumber.EPSILON){var perpSeg2;if(limit>0){if(0>perpSeg1||perpSeg1>limit)return[];if(perpSeg2=seg2dy*seg1seg2dx-seg2dx*seg1seg2dy,0>perpSeg2||perpSeg2>limit)return[]}else{if(perpSeg1>0||limit>perpSeg1)return[];if(perpSeg2=seg2dy*seg1seg2dx-seg2dx*seg1seg2dy,perpSeg2>0||limit>perpSeg2)return[]}if(0===perpSeg2)return!inExcludeAdjacentSegs||0!==perpSeg1&&perpSeg1!==limit?[inSeg1Pt1]:[];if(perpSeg2===limit)return!inExcludeAdjacentSegs||0!==perpSeg1&&perpSeg1!==limit?[inSeg1Pt2]:[];if(0===perpSeg1)return[inSeg2Pt1];if(perpSeg1===limit)return[inSeg2Pt2];var factorSeg1=perpSeg2/limit;return[{x:inSeg1Pt1.x+factorSeg1*seg1dx,y:inSeg1Pt1.y+factorSeg1*seg1dy}]}if(0!==perpSeg1||seg2dy*seg1seg2dx!==seg2dx*seg1seg2dy)return[];var seg1Pt=0===seg1dx&&0===seg1dy,seg2Pt=0===seg2dx&&0===seg2dy;if(seg1Pt&&seg2Pt)return inSeg1Pt1.x!==inSeg2Pt1.x||inSeg1Pt1.y!==inSeg2Pt1.y?[]:[inSeg1Pt1];if(seg1Pt)return point_in_segment_2D_colin(inSeg2Pt1,inSeg2Pt2,inSeg1Pt1)?[inSeg1Pt1]:[];if(seg2Pt)return point_in_segment_2D_colin(inSeg1Pt1,inSeg1Pt2,inSeg2Pt1)?[inSeg2Pt1]:[];var seg1min,seg1max,seg1minVal,seg1maxVal,seg2min,seg2max,seg2minVal,seg2maxVal;return 0!==seg1dx?(inSeg1Pt1.x=seg1minVal?seg2minVal>seg1maxVal?[]:seg1maxVal===seg2minVal?inExcludeAdjacentSegs?[]:[seg2min]:seg2maxVal>=seg1maxVal?[seg2min,seg1max]:[seg2min,seg2max]:seg1minVal>seg2maxVal?[]:seg1minVal===seg2maxVal?inExcludeAdjacentSegs?[]:[seg1min]:seg2maxVal>=seg1maxVal?[seg1min,seg1max]:[seg1min,seg2max]}function isPointInsideAngle(inVertex,inLegFromPt,inLegToPt,inOtherPt){var legFromPtX=inLegFromPt.x-inVertex.x,legFromPtY=inLegFromPt.y-inVertex.y,legToPtX=inLegToPt.x-inVertex.x,legToPtY=inLegToPt.y-inVertex.y,otherPtX=inOtherPt.x-inVertex.x,otherPtY=inOtherPt.y-inVertex.y,from2toAngle=legFromPtX*legToPtY-legFromPtY*legToPtX,from2otherAngle=legFromPtX*otherPtY-legFromPtY*otherPtX;if(Math.abs(from2toAngle)>Number.EPSILON){var other2toAngle=otherPtX*legToPtY-otherPtY*legToPtX;return from2toAngle>0?from2otherAngle>=0&&other2toAngle>=0:from2otherAngle>=0||other2toAngle>=0}return from2otherAngle>0}function removeHoles(contour,holes){function isCutLineInsideAngles(inShapeIdx,inHoleIdx){var lastShapeIdx=shape.length-1,prevShapeIdx=inShapeIdx-1;0>prevShapeIdx&&(prevShapeIdx=lastShapeIdx);var nextShapeIdx=inShapeIdx+1;nextShapeIdx>lastShapeIdx&&(nextShapeIdx=0);var insideAngle=isPointInsideAngle(shape[inShapeIdx],shape[prevShapeIdx],shape[nextShapeIdx],hole[inHoleIdx]);if(!insideAngle)return!1;var lastHoleIdx=hole.length-1,prevHoleIdx=inHoleIdx-1;0>prevHoleIdx&&(prevHoleIdx=lastHoleIdx);var nextHoleIdx=inHoleIdx+1;return nextHoleIdx>lastHoleIdx&&(nextHoleIdx=0),insideAngle=isPointInsideAngle(hole[inHoleIdx],hole[prevHoleIdx],hole[nextHoleIdx],shape[inShapeIdx]),!!insideAngle}function intersectsShapeEdge(inShapePt,inHolePt){var sIdx,nextIdx,intersection;for(sIdx=0;sIdx0)return!0;return!1}function intersectsHoleEdge(inShapePt,inHolePt){var ihIdx,chkHole,hIdx,nextIdx,intersection;for(ihIdx=0;ihIdx0)return!0;return!1}for(var hole,holeIndex,shapeIndex,shapePt,holePt,holeIdx,cutKey,tmpShape1,tmpShape2,tmpHole1,tmpHole2,shape=contour.concat(),indepHoles=[],failedCuts=[],h=0,hl=holes.length;hl>h;h++)indepHoles.push(h);for(var minShapeIndex=0,counter=2*indepHoles.length;indepHoles.length>0;){if(counter--,0>counter){console.log("Infinite Loop! Holes left:"+indepHoles.length+", Probably Hole outside Shape!");break}for(shapeIndex=minShapeIndex;shapeIndex=0)break;failedCuts[cutKey]=!0}if(holeIndex>=0)break}}return shape}for(var i,il,f,face,key,index,allPointsMap={},allpoints=contour.concat(),h=0,hl=holes.length;hl>h;h++)Array.prototype.push.apply(allpoints,holes[h]);for(i=0,il=allpoints.length;il>i;i++)key=allpoints[i].x+":"+allpoints[i].y,void 0!==allPointsMap[key]&&console.warn("THREE.Shape: Duplicate point",key),allPointsMap[key]=i;var shapeWithoutHoles=removeHoles(contour,holes),triangles=THREE.ShapeUtils.triangulate(shapeWithoutHoles,!1);for(i=0,il=triangles.length;il>i;i++)for(face=triangles[i],f=0;3>f;f++)key=face[f].x+":"+face[f].y,index=allPointsMap[key],void 0!==index&&(face[f]=index);return triangles.concat()},isClockWise:function(pts){return THREE.ShapeUtils.area(pts)<0},b2:function(){function b2p0(t,p){var k=1-t;return k*k*p}function b2p1(t,p){return 2*(1-t)*t*p}function b2p2(t,p){return t*t*p}return function(t,p0,p1,p2){return b2p0(t,p0)+b2p1(t,p1)+b2p2(t,p2)}}(),b3:function(){function b3p0(t,p){var k=1-t;return k*k*k*p}function b3p1(t,p){var k=1-t;return 3*k*k*t*p}function b3p2(t,p){var k=1-t;return 3*k*t*t*p}function b3p3(t,p){return t*t*t*p}return function(t,p0,p1,p2,p3){return b3p0(t,p0)+b3p1(t,p1)+b3p2(t,p2)+b3p3(t,p3)}}()},THREE.Audio=function(listener){THREE.Object3D.call(this),this.type="Audio",this.context=listener.context,this.source=this.context.createBufferSource(),this.source.onended=this.onEnded.bind(this),this.gain=this.context.createGain(),this.gain.connect(this.context.destination),this.panner=this.context.createPanner(),this.panner.connect(this.gain),this.autoplay=!1,this.startTime=0,this.playbackRate=1,this.isPlaying=!1},THREE.Audio.prototype=Object.create(THREE.Object3D.prototype),THREE.Audio.prototype.constructor=THREE.Audio,THREE.Audio.prototype.load=function(file){var scope=this,request=new XMLHttpRequest;return request.open("GET",file,!0),request.responseType="arraybuffer",request.onload=function(e){scope.context.decodeAudioData(this.response,function(buffer){scope.source.buffer=buffer,scope.autoplay&&scope.play()})},request.send(),this},THREE.Audio.prototype.play=function(){if(this.isPlaying===!0)return void console.warn("THREE.Audio: Audio is already playing.");var source=this.context.createBufferSource();source.buffer=this.source.buffer,source.loop=this.source.loop,source.onended=this.source.onended,source.start(0,this.startTime),source.playbackRate.value=this.playbackRate,this.isPlaying=!0,this.source=source,this.connect()},THREE.Audio.prototype.pause=function(){this.source.stop(),this.startTime=this.context.currentTime},THREE.Audio.prototype.stop=function(){this.source.stop(),this.startTime=0},THREE.Audio.prototype.connect=function(){void 0!==this.filter?(this.source.connect(this.filter),this.filter.connect(this.panner)):this.source.connect(this.panner)},THREE.Audio.prototype.disconnect=function(){void 0!==this.filter?(this.source.disconnect(this.filter),this.filter.disconnect(this.panner)):this.source.disconnect(this.panner)},THREE.Audio.prototype.setFilter=function(value){this.isPlaying===!0?(this.disconnect(),this.filter=value,this.connect()):this.filter=value},THREE.Audio.prototype.getFilter=function(){return this.filter},THREE.Audio.prototype.setPlaybackRate=function(value){this.playbackRate=value,this.isPlaying===!0&&(this.source.playbackRate.value=this.playbackRate)},THREE.Audio.prototype.getPlaybackRate=function(){return this.playbackRate},THREE.Audio.prototype.onEnded=function(){this.isPlaying=!1},THREE.Audio.prototype.setLoop=function(value){this.source.loop=value},THREE.Audio.prototype.getLoop=function(){return this.source.loop},THREE.Audio.prototype.setRefDistance=function(value){this.panner.refDistance=value},THREE.Audio.prototype.getRefDistance=function(){return this.panner.refDistance},THREE.Audio.prototype.setRolloffFactor=function(value){this.panner.rolloffFactor=value},THREE.Audio.prototype.getRolloffFactor=function(){return this.panner.rolloffFactor},THREE.Audio.prototype.setVolume=function(value){this.gain.gain.value=value},THREE.Audio.prototype.getVolume=function(){return this.gain.gain.value},THREE.Audio.prototype.updateMatrixWorld=function(){var position=new THREE.Vector3;return function(force){THREE.Object3D.prototype.updateMatrixWorld.call(this,force),position.setFromMatrixPosition(this.matrixWorld),this.panner.setPosition(position.x,position.y,position.z)}}(),THREE.AudioListener=function(){THREE.Object3D.call(this),this.type="AudioListener",this.context=new(window.AudioContext||window.webkitAudioContext)},THREE.AudioListener.prototype=Object.create(THREE.Object3D.prototype),THREE.AudioListener.prototype.constructor=THREE.AudioListener,THREE.AudioListener.prototype.updateMatrixWorld=function(){var position=new THREE.Vector3,quaternion=new THREE.Quaternion,scale=new THREE.Vector3,orientation=new THREE.Vector3;return function(force){THREE.Object3D.prototype.updateMatrixWorld.call(this,force);var listener=this.context.listener,up=this.up;this.matrixWorld.decompose(position,quaternion,scale),orientation.set(0,0,-1).applyQuaternion(quaternion),listener.setPosition(position.x,position.y,position.z),listener.setOrientation(orientation.x,orientation.y,orientation.z,up.x,up.y,up.z)}}(),THREE.Curve=function(){},THREE.Curve.prototype={constructor:THREE.Curve,getPoint:function(t){return console.warn("THREE.Curve: Warning, getPoint() not implemented!"),null},getPointAt:function(u){var t=this.getUtoTmapping(u);return this.getPoint(t)},getPoints:function(divisions){divisions||(divisions=5);var d,pts=[];for(d=0;divisions>=d;d++)pts.push(this.getPoint(d/divisions));return pts},getSpacedPoints:function(divisions){divisions||(divisions=5);var d,pts=[];for(d=0;divisions>=d;d++)pts.push(this.getPointAt(d/divisions));return pts},getLength:function(){var lengths=this.getLengths();return lengths[lengths.length-1]},getLengths:function(divisions){if(divisions||(divisions=this.__arcLengthDivisions?this.__arcLengthDivisions:200),this.cacheArcLengths&&this.cacheArcLengths.length===divisions+1&&!this.needsUpdate)return this.cacheArcLengths;this.needsUpdate=!1;var current,p,cache=[],last=this.getPoint(0),sum=0;for(cache.push(0),p=1;divisions>=p;p++)current=this.getPoint(p/divisions),sum+=current.distanceTo(last),cache.push(sum),last=current;return this.cacheArcLengths=cache,cache},updateArcLengths:function(){this.needsUpdate=!0,this.getLengths()},getUtoTmapping:function(u,distance){var targetArcLength,arcLengths=this.getLengths(),i=0,il=arcLengths.length;targetArcLength=distance?distance:u*arcLengths[il-1];for(var comparison,low=0,high=il-1;high>=low;)if(i=Math.floor(low+(high-low)/2),comparison=arcLengths[i]-targetArcLength,0>comparison)low=i+1;else{if(!(comparison>0)){high=i;break}high=i-1}if(i=high,arcLengths[i]===targetArcLength){var t=i/(il-1);return t}var lengthBefore=arcLengths[i],lengthAfter=arcLengths[i+1],segmentLength=lengthAfter-lengthBefore,segmentFraction=(targetArcLength-lengthBefore)/segmentLength,t=(i+segmentFraction)/(il-1);return t},getTangent:function(t){var delta=1e-4,t1=t-delta,t2=t+delta;0>t1&&(t1=0),t2>1&&(t2=1);var pt1=this.getPoint(t1),pt2=this.getPoint(t2),vec=pt2.clone().sub(pt1);return vec.normalize()},getTangentAt:function(u){var t=this.getUtoTmapping(u);return this.getTangent(t)}},THREE.Curve.Utils=THREE.CurveUtils,THREE.Curve.create=function(constructor,getPointFunc){return constructor.prototype=Object.create(THREE.Curve.prototype),constructor.prototype.constructor=constructor,constructor.prototype.getPoint=getPointFunc,constructor},THREE.CurvePath=function(){this.curves=[],this.autoClose=!1},THREE.CurvePath.prototype=Object.create(THREE.Curve.prototype),THREE.CurvePath.prototype.constructor=THREE.CurvePath,THREE.CurvePath.prototype.add=function(curve){this.curves.push(curve)},THREE.CurvePath.prototype.closePath=function(){var startPoint=this.curves[0].getPoint(0),endPoint=this.curves[this.curves.length-1].getPoint(1);startPoint.equals(endPoint)||this.curves.push(new THREE.LineCurve(endPoint,startPoint))},THREE.CurvePath.prototype.getPoint=function(t){for(var d=t*this.getLength(),curveLengths=this.getCurveLengths(),i=0;i=d){var diff=curveLengths[i]-d,curve=this.curves[i],u=1-diff/curve.getLength();return curve.getPointAt(u)}i++}return null},THREE.CurvePath.prototype.getLength=function(){var lens=this.getCurveLengths();return lens[lens.length-1]},THREE.CurvePath.prototype.getCurveLengths=function(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths;for(var lengths=[],sums=0,i=0,l=this.curves.length;l>i;i++)sums+=this.curves[i].getLength(),lengths.push(sums);return this.cacheLengths=lengths,lengths},THREE.CurvePath.prototype.createPointsGeometry=function(divisions){var pts=this.getPoints(divisions,!0);return this.createGeometry(pts)},THREE.CurvePath.prototype.createSpacedPointsGeometry=function(divisions){var pts=this.getSpacedPoints(divisions,!0);return this.createGeometry(pts)},THREE.CurvePath.prototype.createGeometry=function(points){for(var geometry=new THREE.Geometry,i=0,l=points.length;l>i;i++){var point=points[i];geometry.vertices.push(new THREE.Vector3(point.x,point.y,point.z||0))}return geometry},THREE.Path=function(points){THREE.CurvePath.call(this),this.actions=[],points&&this.fromPoints(points)},THREE.Path.prototype=Object.create(THREE.CurvePath.prototype),THREE.Path.prototype.constructor=THREE.Path,THREE.Path.prototype.fromPoints=function(vectors){this.moveTo(vectors[0].x,vectors[0].y);for(var i=1,l=vectors.length;l>i;i++)this.lineTo(vectors[i].x,vectors[i].y)},THREE.Path.prototype.moveTo=function(x,y){this.actions.push({action:"moveTo",args:[x,y]})},THREE.Path.prototype.lineTo=function(x,y){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1],curve=new THREE.LineCurve(new THREE.Vector2(x0,y0),new THREE.Vector2(x,y));this.curves.push(curve),this.actions.push({action:"lineTo",args:[x,y]})},THREE.Path.prototype.quadraticCurveTo=function(aCPx,aCPy,aX,aY){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1],curve=new THREE.QuadraticBezierCurve(new THREE.Vector2(x0,y0),new THREE.Vector2(aCPx,aCPy),new THREE.Vector2(aX,aY));this.curves.push(curve),this.actions.push({action:"quadraticCurveTo",args:[aCPx,aCPy,aX,aY]})},THREE.Path.prototype.bezierCurveTo=function(aCP1x,aCP1y,aCP2x,aCP2y,aX,aY){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1],curve=new THREE.CubicBezierCurve(new THREE.Vector2(x0,y0),new THREE.Vector2(aCP1x,aCP1y),new THREE.Vector2(aCP2x,aCP2y),new THREE.Vector2(aX,aY));this.curves.push(curve),this.actions.push({action:"bezierCurveTo",args:[aCP1x,aCP1y,aCP2x,aCP2y,aX,aY]})},THREE.Path.prototype.splineThru=function(pts){var args=Array.prototype.slice.call(arguments),lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1],npts=[new THREE.Vector2(x0,y0)];Array.prototype.push.apply(npts,pts);var curve=new THREE.SplineCurve(npts);this.curves.push(curve),this.actions.push({action:"splineThru",args:args})},THREE.Path.prototype.arc=function(aX,aY,aRadius,aStartAngle,aEndAngle,aClockwise){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1];this.absarc(aX+x0,aY+y0,aRadius,aStartAngle,aEndAngle,aClockwise)},THREE.Path.prototype.absarc=function(aX,aY,aRadius,aStartAngle,aEndAngle,aClockwise){this.absellipse(aX,aY,aRadius,aRadius,aStartAngle,aEndAngle,aClockwise)},THREE.Path.prototype.ellipse=function(aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1];this.absellipse(aX+x0,aY+y0,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation)},THREE.Path.prototype.absellipse=function(aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation){var args=[aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation||0],curve=new THREE.EllipseCurve(aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation);this.curves.push(curve);var lastPoint=curve.getPoint(1);args.push(lastPoint.x),args.push(lastPoint.y),this.actions.push({action:"ellipse",args:args})},THREE.Path.prototype.getSpacedPoints=function(divisions,closedPath){divisions||(divisions=40);for(var points=[],i=0;divisions>i;i++)points.push(this.getPoint(i/divisions));return points},THREE.Path.prototype.getPoints=function(divisions,closedPath){divisions=divisions||12;for(var cpx,cpy,cpx2,cpy2,cpx1,cpy1,cpx0,cpy0,laste,tx,ty,b2=THREE.ShapeUtils.b2,b3=THREE.ShapeUtils.b3,points=[],i=0,l=this.actions.length;l>i;i++){var item=this.actions[i],action=item.action,args=item.args;switch(action){case"moveTo":points.push(new THREE.Vector2(args[0],args[1]));break;case"lineTo":points.push(new THREE.Vector2(args[0],args[1]));
+break;case"quadraticCurveTo":cpx=args[2],cpy=args[3],cpx1=args[0],cpy1=args[1],points.length>0?(laste=points[points.length-1],cpx0=laste.x,cpy0=laste.y):(laste=this.actions[i-1].args,cpx0=laste[laste.length-2],cpy0=laste[laste.length-1]);for(var j=1;divisions>=j;j++){var t=j/divisions;tx=b2(t,cpx0,cpx1,cpx),ty=b2(t,cpy0,cpy1,cpy),points.push(new THREE.Vector2(tx,ty))}break;case"bezierCurveTo":cpx=args[4],cpy=args[5],cpx1=args[0],cpy1=args[1],cpx2=args[2],cpy2=args[3],points.length>0?(laste=points[points.length-1],cpx0=laste.x,cpy0=laste.y):(laste=this.actions[i-1].args,cpx0=laste[laste.length-2],cpy0=laste[laste.length-1]);for(var j=1;divisions>=j;j++){var t=j/divisions;tx=b3(t,cpx0,cpx1,cpx2,cpx),ty=b3(t,cpy0,cpy1,cpy2,cpy),points.push(new THREE.Vector2(tx,ty))}break;case"splineThru":laste=this.actions[i-1].args;var last=new THREE.Vector2(laste[laste.length-2],laste[laste.length-1]),spts=[last],n=divisions*args[0].length;spts=spts.concat(args[0]);for(var spline=new THREE.SplineCurve(spts),j=1;n>=j;j++)points.push(spline.getPointAt(j/n));break;case"arc":for(var angle,aX=args[0],aY=args[1],aRadius=args[2],aStartAngle=args[3],aEndAngle=args[4],aClockwise=!!args[5],deltaAngle=aEndAngle-aStartAngle,tdivisions=2*divisions,j=1;tdivisions>=j;j++){var t=j/tdivisions;aClockwise||(t=1-t),angle=aStartAngle+t*deltaAngle,tx=aX+aRadius*Math.cos(angle),ty=aY+aRadius*Math.sin(angle),points.push(new THREE.Vector2(tx,ty))}break;case"ellipse":var angle,cos,sin,aX=args[0],aY=args[1],xRadius=args[2],yRadius=args[3],aStartAngle=args[4],aEndAngle=args[5],aClockwise=!!args[6],aRotation=args[7],deltaAngle=aEndAngle-aStartAngle,tdivisions=2*divisions;0!==aRotation&&(cos=Math.cos(aRotation),sin=Math.sin(aRotation));for(var j=1;tdivisions>=j;j++){var t=j/tdivisions;if(aClockwise||(t=1-t),angle=aStartAngle+t*deltaAngle,tx=aX+xRadius*Math.cos(angle),ty=aY+yRadius*Math.sin(angle),0!==aRotation){var x=tx,y=ty;tx=(x-aX)*cos-(y-aY)*sin+aX,ty=(x-aX)*sin+(y-aY)*cos+aY}points.push(new THREE.Vector2(tx,ty))}}}var lastPoint=points[points.length-1];return Math.abs(lastPoint.x-points[0].x)i;i++){var item=inActions[i],args=item.args,action=item.action;"moveTo"===action&&0!==lastPath.actions.length&&(subPaths.push(lastPath),lastPath=new THREE.Path),lastPath[action].apply(lastPath,args)}return 0!==lastPath.actions.length&&subPaths.push(lastPath),subPaths}function toShapesNoHoles(inSubpaths){for(var shapes=[],i=0,l=inSubpaths.length;l>i;i++){var tmpPath=inSubpaths[i],tmpShape=new THREE.Shape;tmpShape.actions=tmpPath.actions,tmpShape.curves=tmpPath.curves,shapes.push(tmpShape)}return shapes}function isPointInsidePolygon(inPt,inPolygon){for(var polyLen=inPolygon.length,inside=!1,p=polyLen-1,q=0;polyLen>q;p=q++){var edgeLowPt=inPolygon[p],edgeHighPt=inPolygon[q],edgeDx=edgeHighPt.x-edgeLowPt.x,edgeDy=edgeHighPt.y-edgeLowPt.y;if(Math.abs(edgeDy)>Number.EPSILON){if(0>edgeDy&&(edgeLowPt=inPolygon[q],edgeDx=-edgeDx,edgeHighPt=inPolygon[p],edgeDy=-edgeDy),inPt.yedgeHighPt.y)continue;if(inPt.y===edgeLowPt.y){if(inPt.x===edgeLowPt.x)return!0}else{var perpEdge=edgeDy*(inPt.x-edgeLowPt.x)-edgeDx*(inPt.y-edgeLowPt.y);if(0===perpEdge)return!0;if(0>perpEdge)continue;inside=!inside}}else{if(inPt.y!==edgeLowPt.y)continue;if(edgeHighPt.x<=inPt.x&&inPt.x<=edgeLowPt.x||edgeLowPt.x<=inPt.x&&inPt.x<=edgeHighPt.x)return!0}}return inside}var isClockWise=THREE.ShapeUtils.isClockWise,subPaths=extractSubpaths(this.actions);if(0===subPaths.length)return[];if(noHoles===!0)return toShapesNoHoles(subPaths);var solid,tmpPath,tmpShape,shapes=[];if(1===subPaths.length)return tmpPath=subPaths[0],tmpShape=new THREE.Shape,tmpShape.actions=tmpPath.actions,tmpShape.curves=tmpPath.curves,shapes.push(tmpShape),shapes;var holesFirst=!isClockWise(subPaths[0].getPoints());holesFirst=isCCW?!holesFirst:holesFirst;var tmpPoints,betterShapeHoles=[],newShapes=[],newShapeHoles=[],mainIdx=0;newShapes[mainIdx]=void 0,newShapeHoles[mainIdx]=[];for(var i=0,l=subPaths.length;l>i;i++)tmpPath=subPaths[i],tmpPoints=tmpPath.getPoints(),solid=isClockWise(tmpPoints),solid=isCCW?!solid:solid,solid?(!holesFirst&&newShapes[mainIdx]&&mainIdx++,newShapes[mainIdx]={s:new THREE.Shape,p:tmpPoints},newShapes[mainIdx].s.actions=tmpPath.actions,newShapes[mainIdx].s.curves=tmpPath.curves,holesFirst&&mainIdx++,newShapeHoles[mainIdx]=[]):newShapeHoles[mainIdx].push({h:tmpPath,p:tmpPoints[0]});if(!newShapes[0])return toShapesNoHoles(subPaths);if(newShapes.length>1){for(var ambiguous=!1,toChange=[],sIdx=0,sLen=newShapes.length;sLen>sIdx;sIdx++)betterShapeHoles[sIdx]=[];for(var sIdx=0,sLen=newShapes.length;sLen>sIdx;sIdx++)for(var sho=newShapeHoles[sIdx],hIdx=0;hIdx0&&(ambiguous||(newShapeHoles=betterShapeHoles))}for(var tmpHoles,i=0,il=newShapes.length;il>i;i++){tmpShape=newShapes[i].s,shapes.push(tmpShape),tmpHoles=newShapeHoles[i];for(var j=0,jl=tmpHoles.length;jl>j;j++)tmpShape.holes.push(tmpHoles[j].h)}return shapes},THREE.Shape=function(){THREE.Path.apply(this,arguments),this.holes=[]},THREE.Shape.prototype=Object.create(THREE.Path.prototype),THREE.Shape.prototype.constructor=THREE.Shape,THREE.Shape.prototype.extrude=function(options){return new THREE.ExtrudeGeometry(this,options)},THREE.Shape.prototype.makeGeometry=function(options){return new THREE.ShapeGeometry(this,options)},THREE.Shape.prototype.getPointsHoles=function(divisions){for(var holesPts=[],i=0,l=this.holes.length;l>i;i++)holesPts[i]=this.holes[i].getPoints(divisions);return holesPts},THREE.Shape.prototype.extractAllPoints=function(divisions){return{shape:this.getPoints(divisions),holes:this.getPointsHoles(divisions)}},THREE.Shape.prototype.extractPoints=function(divisions){return this.extractAllPoints(divisions)},THREE.Shape.Utils=THREE.ShapeUtils,THREE.LineCurve=function(v1,v2){this.v1=v1,this.v2=v2},THREE.LineCurve.prototype=Object.create(THREE.Curve.prototype),THREE.LineCurve.prototype.constructor=THREE.LineCurve,THREE.LineCurve.prototype.getPoint=function(t){var point=this.v2.clone().sub(this.v1);return point.multiplyScalar(t).add(this.v1),point},THREE.LineCurve.prototype.getPointAt=function(u){return this.getPoint(u)},THREE.LineCurve.prototype.getTangent=function(t){var tangent=this.v2.clone().sub(this.v1);return tangent.normalize()},THREE.QuadraticBezierCurve=function(v0,v1,v2){this.v0=v0,this.v1=v1,this.v2=v2},THREE.QuadraticBezierCurve.prototype=Object.create(THREE.Curve.prototype),THREE.QuadraticBezierCurve.prototype.constructor=THREE.QuadraticBezierCurve,THREE.QuadraticBezierCurve.prototype.getPoint=function(t){var b2=THREE.ShapeUtils.b2;return new THREE.Vector2(b2(t,this.v0.x,this.v1.x,this.v2.x),b2(t,this.v0.y,this.v1.y,this.v2.y))},THREE.QuadraticBezierCurve.prototype.getTangent=function(t){var tangentQuadraticBezier=THREE.CurveUtils.tangentQuadraticBezier;return new THREE.Vector2(tangentQuadraticBezier(t,this.v0.x,this.v1.x,this.v2.x),tangentQuadraticBezier(t,this.v0.y,this.v1.y,this.v2.y)).normalize()},THREE.CubicBezierCurve=function(v0,v1,v2,v3){this.v0=v0,this.v1=v1,this.v2=v2,this.v3=v3},THREE.CubicBezierCurve.prototype=Object.create(THREE.Curve.prototype),THREE.CubicBezierCurve.prototype.constructor=THREE.CubicBezierCurve,THREE.CubicBezierCurve.prototype.getPoint=function(t){var b3=THREE.ShapeUtils.b3;return new THREE.Vector2(b3(t,this.v0.x,this.v1.x,this.v2.x,this.v3.x),b3(t,this.v0.y,this.v1.y,this.v2.y,this.v3.y))},THREE.CubicBezierCurve.prototype.getTangent=function(t){var tangentCubicBezier=THREE.CurveUtils.tangentCubicBezier;return new THREE.Vector2(tangentCubicBezier(t,this.v0.x,this.v1.x,this.v2.x,this.v3.x),tangentCubicBezier(t,this.v0.y,this.v1.y,this.v2.y,this.v3.y)).normalize()},THREE.SplineCurve=function(points){this.points=void 0==points?[]:points},THREE.SplineCurve.prototype=Object.create(THREE.Curve.prototype),THREE.SplineCurve.prototype.constructor=THREE.SplineCurve,THREE.SplineCurve.prototype.getPoint=function(t){var points=this.points,point=(points.length-1)*t,intPoint=Math.floor(point),weight=point-intPoint,point0=points[0===intPoint?intPoint:intPoint-1],point1=points[intPoint],point2=points[intPoint>points.length-2?points.length-1:intPoint+1],point3=points[intPoint>points.length-3?points.length-1:intPoint+2],interpolate=THREE.CurveUtils.interpolate;return new THREE.Vector2(interpolate(point0.x,point1.x,point2.x,point3.x,weight),interpolate(point0.y,point1.y,point2.y,point3.y,weight))},THREE.EllipseCurve=function(aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation){this.aX=aX,this.aY=aY,this.xRadius=xRadius,this.yRadius=yRadius,this.aStartAngle=aStartAngle,this.aEndAngle=aEndAngle,this.aClockwise=aClockwise,this.aRotation=aRotation||0},THREE.EllipseCurve.prototype=Object.create(THREE.Curve.prototype),THREE.EllipseCurve.prototype.constructor=THREE.EllipseCurve,THREE.EllipseCurve.prototype.getPoint=function(t){var deltaAngle=this.aEndAngle-this.aStartAngle;0>deltaAngle&&(deltaAngle+=2*Math.PI),deltaAngle>2*Math.PI&&(deltaAngle-=2*Math.PI);var angle;angle=this.aClockwise===!0?this.aEndAngle+(1-t)*(2*Math.PI-deltaAngle):this.aStartAngle+t*deltaAngle;var x=this.aX+this.xRadius*Math.cos(angle),y=this.aY+this.yRadius*Math.sin(angle);if(0!==this.aRotation){var cos=Math.cos(this.aRotation),sin=Math.sin(this.aRotation),tx=x,ty=y;x=(tx-this.aX)*cos-(ty-this.aY)*sin+this.aX,y=(tx-this.aX)*sin+(ty-this.aY)*cos+this.aY}return new THREE.Vector2(x,y)},THREE.ArcCurve=function(aX,aY,aRadius,aStartAngle,aEndAngle,aClockwise){THREE.EllipseCurve.call(this,aX,aY,aRadius,aRadius,aStartAngle,aEndAngle,aClockwise)},THREE.ArcCurve.prototype=Object.create(THREE.EllipseCurve.prototype),THREE.ArcCurve.prototype.constructor=THREE.ArcCurve,THREE.LineCurve3=THREE.Curve.create(function(v1,v2){this.v1=v1,this.v2=v2},function(t){var vector=new THREE.Vector3;return vector.subVectors(this.v2,this.v1),vector.multiplyScalar(t),vector.add(this.v1),vector}),THREE.QuadraticBezierCurve3=THREE.Curve.create(function(v0,v1,v2){this.v0=v0,this.v1=v1,this.v2=v2},function(t){var b2=THREE.ShapeUtils.b2;return new THREE.Vector3(b2(t,this.v0.x,this.v1.x,this.v2.x),b2(t,this.v0.y,this.v1.y,this.v2.y),b2(t,this.v0.z,this.v1.z,this.v2.z))}),THREE.CubicBezierCurve3=THREE.Curve.create(function(v0,v1,v2,v3){this.v0=v0,this.v1=v1,this.v2=v2,this.v3=v3},function(t){var b3=THREE.ShapeUtils.b3;return new THREE.Vector3(b3(t,this.v0.x,this.v1.x,this.v2.x,this.v3.x),b3(t,this.v0.y,this.v1.y,this.v2.y,this.v3.y),b3(t,this.v0.z,this.v1.z,this.v2.z,this.v3.z))}),THREE.SplineCurve3=THREE.Curve.create(function(points){console.warn("THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3"),this.points=void 0==points?[]:points},function(t){var points=this.points,point=(points.length-1)*t,intPoint=Math.floor(point),weight=point-intPoint,point0=points[0==intPoint?intPoint:intPoint-1],point1=points[intPoint],point2=points[intPoint>points.length-2?points.length-1:intPoint+1],point3=points[intPoint>points.length-3?points.length-1:intPoint+2],interpolate=THREE.CurveUtils.interpolate;return new THREE.Vector3(interpolate(point0.x,point1.x,point2.x,point3.x,weight),interpolate(point0.y,point1.y,point2.y,point3.y,weight),interpolate(point0.z,point1.z,point2.z,point3.z,weight))}),THREE.CatmullRomCurve3=function(){function CubicPoly(){}var tmp=new THREE.Vector3,px=new CubicPoly,py=new CubicPoly,pz=new CubicPoly;return CubicPoly.prototype.init=function(x0,x1,t0,t1){this.c0=x0,this.c1=t0,this.c2=-3*x0+3*x1-2*t0-t1,this.c3=2*x0-2*x1+t0+t1},CubicPoly.prototype.initNonuniformCatmullRom=function(x0,x1,x2,x3,dt0,dt1,dt2){var t1=(x1-x0)/dt0-(x2-x0)/(dt0+dt1)+(x2-x1)/dt1,t2=(x2-x1)/dt1-(x3-x1)/(dt1+dt2)+(x3-x2)/dt2;t1*=dt1,t2*=dt1,this.init(x1,x2,t1,t2)},CubicPoly.prototype.initCatmullRom=function(x0,x1,x2,x3,tension){this.init(x1,x2,tension*(x2-x0),tension*(x3-x1))},CubicPoly.prototype.calc=function(t){var t2=t*t,t3=t2*t;return this.c0+this.c1*t+this.c2*t2+this.c3*t3},THREE.Curve.create(function(p){this.points=p||[]},function(t){var point,intPoint,weight,l,points=this.points;l=points.length,2>l&&console.log("duh, you need at least 2 points"),point=(l-1)*t,intPoint=Math.floor(point),weight=point-intPoint,0===weight&&intPoint===l-1&&(intPoint=l-2,weight=1);var p0,p1,p2,p3;if(0===intPoint?(tmp.subVectors(points[0],points[1]).add(points[0]),p0=tmp):p0=points[intPoint-1],p1=points[intPoint],p2=points[intPoint+1],l>intPoint+2?p3=points[intPoint+2]:(tmp.subVectors(points[l-1],points[l-2]).add(points[l-2]),p3=tmp),void 0===this.type||"centripetal"===this.type||"chordal"===this.type){var pow="chordal"===this.type?.5:.25,dt0=Math.pow(p0.distanceToSquared(p1),pow),dt1=Math.pow(p1.distanceToSquared(p2),pow),dt2=Math.pow(p2.distanceToSquared(p3),pow);1e-4>dt1&&(dt1=1),1e-4>dt0&&(dt0=dt1),1e-4>dt2&&(dt2=dt1),px.initNonuniformCatmullRom(p0.x,p1.x,p2.x,p3.x,dt0,dt1,dt2),py.initNonuniformCatmullRom(p0.y,p1.y,p2.y,p3.y,dt0,dt1,dt2),pz.initNonuniformCatmullRom(p0.z,p1.z,p2.z,p3.z,dt0,dt1,dt2)}else if("catmullrom"===this.type){var tension=void 0!==this.tension?this.tension:.5;px.initCatmullRom(p0.x,p1.x,p2.x,p3.x,tension),py.initCatmullRom(p0.y,p1.y,p2.y,p3.y,tension),pz.initCatmullRom(p0.z,p1.z,p2.z,p3.z,tension)}var v=new THREE.Vector3(px.calc(weight),py.calc(weight),pz.calc(weight));return v})}(),THREE.ClosedSplineCurve3=THREE.Curve.create(function(points){this.points=void 0==points?[]:points},function(t){var points=this.points,point=(points.length-0)*t,intPoint=Math.floor(point),weight=point-intPoint;intPoint+=intPoint>0?0:(Math.floor(Math.abs(intPoint)/points.length)+1)*points.length;var point0=points[(intPoint-1)%points.length],point1=points[intPoint%points.length],point2=points[(intPoint+1)%points.length],point3=points[(intPoint+2)%points.length],interpolate=THREE.CurveUtils.interpolate;return new THREE.Vector3(interpolate(point0.x,point1.x,point2.x,point3.x,weight),interpolate(point0.y,point1.y,point2.y,point3.y,weight),interpolate(point0.z,point1.z,point2.z,point3.z,weight))}),THREE.BoxGeometry=function(width,height,depth,widthSegments,heightSegments,depthSegments){function buildPlane(u,v,udir,vdir,width,height,depth,materialIndex){var w,ix,iy,gridX=scope.widthSegments,gridY=scope.heightSegments,width_half=width/2,height_half=height/2,offset=scope.vertices.length;"x"===u&&"y"===v||"y"===u&&"x"===v?w="z":"x"===u&&"z"===v||"z"===u&&"x"===v?(w="y",gridY=scope.depthSegments):("z"===u&&"y"===v||"y"===u&&"z"===v)&&(w="x",gridX=scope.depthSegments);var gridX1=gridX+1,gridY1=gridY+1,segment_width=width/gridX,segment_height=height/gridY,normal=new THREE.Vector3;for(normal[w]=depth>0?1:-1,iy=0;gridY1>iy;iy++)for(ix=0;gridX1>ix;ix++){var vector=new THREE.Vector3;vector[u]=(ix*segment_width-width_half)*udir,vector[v]=(iy*segment_height-height_half)*vdir,vector[w]=depth,scope.vertices.push(vector)}for(iy=0;gridY>iy;iy++)for(ix=0;gridX>ix;ix++){var a=ix+gridX1*iy,b=ix+gridX1*(iy+1),c=ix+1+gridX1*(iy+1),d=ix+1+gridX1*iy,uva=new THREE.Vector2(ix/gridX,1-iy/gridY),uvb=new THREE.Vector2(ix/gridX,1-(iy+1)/gridY),uvc=new THREE.Vector2((ix+1)/gridX,1-(iy+1)/gridY),uvd=new THREE.Vector2((ix+1)/gridX,1-iy/gridY),face=new THREE.Face3(a+offset,b+offset,d+offset);face.normal.copy(normal),face.vertexNormals.push(normal.clone(),normal.clone(),normal.clone()),face.materialIndex=materialIndex,scope.faces.push(face),scope.faceVertexUvs[0].push([uva,uvb,uvd]),face=new THREE.Face3(b+offset,c+offset,d+offset),face.normal.copy(normal),face.vertexNormals.push(normal.clone(),normal.clone(),normal.clone()),face.materialIndex=materialIndex,scope.faces.push(face),scope.faceVertexUvs[0].push([uvb.clone(),uvc,uvd.clone()])}}THREE.Geometry.call(this),this.type="BoxGeometry",this.parameters={width:width,height:height,depth:depth,widthSegments:widthSegments,heightSegments:heightSegments,depthSegments:depthSegments},this.widthSegments=widthSegments||1,this.heightSegments=heightSegments||1,this.depthSegments=depthSegments||1;var scope=this,width_half=width/2,height_half=height/2,depth_half=depth/2;buildPlane("z","y",-1,-1,depth,height,width_half,0),buildPlane("z","y",1,-1,depth,height,-width_half,1),buildPlane("x","z",1,1,width,depth,height_half,2),buildPlane("x","z",1,-1,width,depth,-height_half,3),buildPlane("x","y",1,-1,width,height,depth_half,4),buildPlane("x","y",-1,-1,width,height,-depth_half,5),this.mergeVertices()},THREE.BoxGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.BoxGeometry.prototype.constructor=THREE.BoxGeometry,THREE.BoxGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.BoxGeometry(parameters.width,parameters.height,parameters.depth,parameters.widthSegments,parameters.heightSegments,parameters.depthSegments)},THREE.CubeGeometry=THREE.BoxGeometry,THREE.CircleGeometry=function(radius,segments,thetaStart,thetaLength){THREE.Geometry.call(this),this.type="CircleGeometry",this.parameters={radius:radius,segments:segments,thetaStart:thetaStart,thetaLength:thetaLength},this.fromBufferGeometry(new THREE.CircleBufferGeometry(radius,segments,thetaStart,thetaLength))},THREE.CircleGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.CircleGeometry.prototype.constructor=THREE.CircleGeometry,THREE.CircleGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.CircleGeometry(parameters.radius,parameters.segments,parameters.thetaStart,parameters.thetaLength)},THREE.CircleBufferGeometry=function(radius,segments,thetaStart,thetaLength){THREE.BufferGeometry.call(this),this.type="CircleBufferGeometry",this.parameters={radius:radius,segments:segments,thetaStart:thetaStart,thetaLength:thetaLength},radius=radius||50,segments=void 0!==segments?Math.max(3,segments):8,thetaStart=void 0!==thetaStart?thetaStart:0,thetaLength=void 0!==thetaLength?thetaLength:2*Math.PI;var vertices=segments+2,positions=new Float32Array(3*vertices),normals=new Float32Array(3*vertices),uvs=new Float32Array(2*vertices);normals[2]=1,uvs[0]=.5,uvs[1]=.5;for(var s=0,i=3,ii=2;segments>=s;s++,i+=3,ii+=2){var segment=thetaStart+s/segments*thetaLength;positions[i]=radius*Math.cos(segment),positions[i+1]=radius*Math.sin(segment),normals[i+2]=1,uvs[ii]=(positions[i]/radius+1)/2,uvs[ii+1]=(positions[i+1]/radius+1)/2}for(var indices=[],i=1;segments>=i;i++)indices.push(i,i+1,0);this.setIndex(new THREE.BufferAttribute(new Uint16Array(indices),1)),this.addAttribute("position",new THREE.BufferAttribute(positions,3)),this.addAttribute("normal",new THREE.BufferAttribute(normals,3)),this.addAttribute("uv",new THREE.BufferAttribute(uvs,2)),this.boundingSphere=new THREE.Sphere(new THREE.Vector3,radius)},THREE.CircleBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.CircleBufferGeometry.prototype.constructor=THREE.CircleBufferGeometry,THREE.CircleBufferGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.CircleBufferGeometry(parameters.radius,parameters.segments,parameters.thetaStart,parameters.thetaLength)},THREE.CylinderGeometry=function(radiusTop,radiusBottom,height,radialSegments,heightSegments,openEnded,thetaStart,thetaLength){THREE.Geometry.call(this),this.type="CylinderGeometry",this.parameters={radiusTop:radiusTop,radiusBottom:radiusBottom,height:height,radialSegments:radialSegments,heightSegments:heightSegments,openEnded:openEnded,thetaStart:thetaStart,thetaLength:thetaLength},radiusTop=void 0!==radiusTop?radiusTop:20,radiusBottom=void 0!==radiusBottom?radiusBottom:20,height=void 0!==height?height:100,radialSegments=radialSegments||8,heightSegments=heightSegments||1,openEnded=void 0!==openEnded?openEnded:!1,thetaStart=void 0!==thetaStart?thetaStart:0,thetaLength=void 0!==thetaLength?thetaLength:2*Math.PI;var x,y,heightHalf=height/2,vertices=[],uvs=[];for(y=0;heightSegments>=y;y++){var verticesRow=[],uvsRow=[],v=y/heightSegments,radius=v*(radiusBottom-radiusTop)+radiusTop;for(x=0;radialSegments>=x;x++){var u=x/radialSegments,vertex=new THREE.Vector3;vertex.x=radius*Math.sin(u*thetaLength+thetaStart),vertex.y=-v*height+heightHalf,vertex.z=radius*Math.cos(u*thetaLength+thetaStart),this.vertices.push(vertex),verticesRow.push(this.vertices.length-1),uvsRow.push(new THREE.Vector2(u,1-v))}vertices.push(verticesRow),uvs.push(uvsRow)}var na,nb,tanTheta=(radiusBottom-radiusTop)/height;for(x=0;radialSegments>x;x++)for(0!==radiusTop?(na=this.vertices[vertices[0][x]].clone(),nb=this.vertices[vertices[0][x+1]].clone()):(na=this.vertices[vertices[1][x]].clone(),nb=this.vertices[vertices[1][x+1]].clone()),na.setY(Math.sqrt(na.x*na.x+na.z*na.z)*tanTheta).normalize(),nb.setY(Math.sqrt(nb.x*nb.x+nb.z*nb.z)*tanTheta).normalize(),y=0;heightSegments>y;y++){var v1=vertices[y][x],v2=vertices[y+1][x],v3=vertices[y+1][x+1],v4=vertices[y][x+1],n1=na.clone(),n2=na.clone(),n3=nb.clone(),n4=nb.clone(),uv1=uvs[y][x].clone(),uv2=uvs[y+1][x].clone(),uv3=uvs[y+1][x+1].clone(),uv4=uvs[y][x+1].clone();this.faces.push(new THREE.Face3(v1,v2,v4,[n1,n2,n4])),this.faceVertexUvs[0].push([uv1,uv2,uv4]),this.faces.push(new THREE.Face3(v2,v3,v4,[n2.clone(),n3,n4.clone()])),this.faceVertexUvs[0].push([uv2.clone(),uv3,uv4.clone()])}if(openEnded===!1&&radiusTop>0)for(this.vertices.push(new THREE.Vector3(0,heightHalf,0)),x=0;radialSegments>x;x++){var v1=vertices[0][x],v2=vertices[0][x+1],v3=this.vertices.length-1,n1=new THREE.Vector3(0,1,0),n2=new THREE.Vector3(0,1,0),n3=new THREE.Vector3(0,1,0),uv1=uvs[0][x].clone(),uv2=uvs[0][x+1].clone(),uv3=new THREE.Vector2(uv2.x,0);this.faces.push(new THREE.Face3(v1,v2,v3,[n1,n2,n3],void 0,1)),this.faceVertexUvs[0].push([uv1,uv2,uv3])}if(openEnded===!1&&radiusBottom>0)for(this.vertices.push(new THREE.Vector3(0,-heightHalf,0)),x=0;radialSegments>x;x++){var v1=vertices[heightSegments][x+1],v2=vertices[heightSegments][x],v3=this.vertices.length-1,n1=new THREE.Vector3(0,-1,0),n2=new THREE.Vector3(0,-1,0),n3=new THREE.Vector3(0,-1,0),uv1=uvs[heightSegments][x+1].clone(),uv2=uvs[heightSegments][x].clone(),uv3=new THREE.Vector2(uv2.x,1);this.faces.push(new THREE.Face3(v1,v2,v3,[n1,n2,n3],void 0,2)),this.faceVertexUvs[0].push([uv1,uv2,uv3])}this.computeFaceNormals()},THREE.CylinderGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.CylinderGeometry.prototype.constructor=THREE.CylinderGeometry,THREE.CylinderGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.CylinderGeometry(parameters.radiusTop,parameters.radiusBottom,parameters.height,parameters.radialSegments,parameters.heightSegments,parameters.openEnded,parameters.thetaStart,parameters.thetaLength)},THREE.EdgesGeometry=function(geometry,thresholdAngle){function sortFunction(a,b){return a-b}THREE.BufferGeometry.call(this),thresholdAngle=void 0!==thresholdAngle?thresholdAngle:1;var geometry2,thresholdDot=Math.cos(THREE.Math.degToRad(thresholdAngle)),edge=[0,0],hash={},keys=["a","b","c"];geometry instanceof THREE.BufferGeometry?(geometry2=new THREE.Geometry,geometry2.fromBufferGeometry(geometry)):geometry2=geometry.clone(),geometry2.mergeVertices(),geometry2.computeFaceNormals();for(var vertices=geometry2.vertices,faces=geometry2.faces,i=0,l=faces.length;l>i;i++)for(var face=faces[i],j=0;3>j;j++){edge[0]=face[keys[j]],edge[1]=face[keys[(j+1)%3]],edge.sort(sortFunction);var key=edge.toString();void 0===hash[key]?hash[key]={vert1:edge[0],vert2:edge[1],face1:i,face2:void 0}:hash[key].face2=i}var coords=[];for(var key in hash){var h=hash[key];if(void 0===h.face2||faces[h.face1].normal.dot(faces[h.face2].normal)<=thresholdDot){var vertex=vertices[h.vert1];coords.push(vertex.x),coords.push(vertex.y),coords.push(vertex.z),vertex=vertices[h.vert2],coords.push(vertex.x),coords.push(vertex.y),coords.push(vertex.z)}}this.addAttribute("position",new THREE.BufferAttribute(new Float32Array(coords),3))},THREE.EdgesGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.EdgesGeometry.prototype.constructor=THREE.EdgesGeometry,THREE.ExtrudeGeometry=function(shapes,options){return"undefined"==typeof shapes?void(shapes=[]):(THREE.Geometry.call(this),this.type="ExtrudeGeometry",shapes=Array.isArray(shapes)?shapes:[shapes],this.addShapeList(shapes,options),void this.computeFaceNormals())},THREE.ExtrudeGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.ExtrudeGeometry.prototype.constructor=THREE.ExtrudeGeometry,THREE.ExtrudeGeometry.prototype.addShapeList=function(shapes,options){for(var sl=shapes.length,s=0;sl>s;s++){var shape=shapes[s];this.addShape(shape,options)}},THREE.ExtrudeGeometry.prototype.addShape=function(shape,options){function scalePt2(pt,vec,size){return vec||console.error("THREE.ExtrudeGeometry: vec does not exist"),vec.clone().multiplyScalar(size).add(pt)}function getBevelVec(inPt,inPrev,inNext){var v_trans_x,v_trans_y,shrink_by=1,v_prev_x=inPt.x-inPrev.x,v_prev_y=inPt.y-inPrev.y,v_next_x=inNext.x-inPt.x,v_next_y=inNext.y-inPt.y,v_prev_lensq=v_prev_x*v_prev_x+v_prev_y*v_prev_y,collinear0=v_prev_x*v_next_y-v_prev_y*v_next_x;if(Math.abs(collinear0)>Number.EPSILON){var v_prev_len=Math.sqrt(v_prev_lensq),v_next_len=Math.sqrt(v_next_x*v_next_x+v_next_y*v_next_y),ptPrevShift_x=inPrev.x-v_prev_y/v_prev_len,ptPrevShift_y=inPrev.y+v_prev_x/v_prev_len,ptNextShift_x=inNext.x-v_next_y/v_next_len,ptNextShift_y=inNext.y+v_next_x/v_next_len,sf=((ptNextShift_x-ptPrevShift_x)*v_next_y-(ptNextShift_y-ptPrevShift_y)*v_next_x)/(v_prev_x*v_next_y-v_prev_y*v_next_x);v_trans_x=ptPrevShift_x+v_prev_x*sf-inPt.x,v_trans_y=ptPrevShift_y+v_prev_y*sf-inPt.y;var v_trans_lensq=v_trans_x*v_trans_x+v_trans_y*v_trans_y;if(2>=v_trans_lensq)return new THREE.Vector2(v_trans_x,v_trans_y);shrink_by=Math.sqrt(v_trans_lensq/2)}else{var direction_eq=!1;v_prev_x>Number.EPSILON?v_next_x>Number.EPSILON&&(direction_eq=!0):v_prev_x<-Number.EPSILON?v_next_x<-Number.EPSILON&&(direction_eq=!0):Math.sign(v_prev_y)===Math.sign(v_next_y)&&(direction_eq=!0),direction_eq?(v_trans_x=-v_prev_y,v_trans_y=v_prev_x,shrink_by=Math.sqrt(v_prev_lensq)):(v_trans_x=v_prev_x,v_trans_y=v_prev_y,shrink_by=Math.sqrt(v_prev_lensq/2))}return new THREE.Vector2(v_trans_x/shrink_by,v_trans_y/shrink_by)}function buildLidFaces(){if(bevelEnabled){var layer=0,offset=vlen*layer;for(i=0;flen>i;i++)face=faces[i],f3(face[2]+offset,face[1]+offset,face[0]+offset);for(layer=steps+2*bevelSegments,offset=vlen*layer,i=0;flen>i;i++)face=faces[i],f3(face[0]+offset,face[1]+offset,face[2]+offset)}else{for(i=0;flen>i;i++)face=faces[i],f3(face[2],face[1],face[0]);for(i=0;flen>i;i++)face=faces[i],f3(face[0]+vlen*steps,face[1]+vlen*steps,face[2]+vlen*steps)}}function buildSideFaces(){var layeroffset=0;for(sidewalls(contour,layeroffset),layeroffset+=contour.length,h=0,hl=holes.length;hl>h;h++)ahole=holes[h],sidewalls(ahole,layeroffset),layeroffset+=ahole.length}function sidewalls(contour,layeroffset){var j,k;for(i=contour.length;--i>=0;){j=i,k=i-1,0>k&&(k=contour.length-1);var s=0,sl=steps+2*bevelSegments;for(s=0;sl>s;s++){var slen1=vlen*s,slen2=vlen*(s+1),a=layeroffset+j+slen1,b=layeroffset+k+slen1,c=layeroffset+k+slen2,d=layeroffset+j+slen2;f4(a,b,c,d,contour,s,sl,j,k)}}}function v(x,y,z){scope.vertices.push(new THREE.Vector3(x,y,z))}function f3(a,b,c){a+=shapesOffset,b+=shapesOffset,c+=shapesOffset,scope.faces.push(new THREE.Face3(a,b,c,null,null,0));var uvs=uvgen.generateTopUV(scope,a,b,c);scope.faceVertexUvs[0].push(uvs)}function f4(a,b,c,d,wallContour,stepIndex,stepsLength,contourIndex1,contourIndex2){a+=shapesOffset,b+=shapesOffset,c+=shapesOffset,d+=shapesOffset,scope.faces.push(new THREE.Face3(a,b,d,null,null,1)),scope.faces.push(new THREE.Face3(b,c,d,null,null,1));var uvs=uvgen.generateSideWallUV(scope,a,b,c,d);scope.faceVertexUvs[0].push([uvs[0],uvs[1],uvs[3]]),scope.faceVertexUvs[0].push([uvs[1],uvs[2],uvs[3]])}var extrudePts,splineTube,binormal,normal,position2,amount=void 0!==options.amount?options.amount:100,bevelThickness=void 0!==options.bevelThickness?options.bevelThickness:6,bevelSize=void 0!==options.bevelSize?options.bevelSize:bevelThickness-2,bevelSegments=void 0!==options.bevelSegments?options.bevelSegments:3,bevelEnabled=void 0!==options.bevelEnabled?options.bevelEnabled:!0,curveSegments=void 0!==options.curveSegments?options.curveSegments:12,steps=void 0!==options.steps?options.steps:1,extrudePath=options.extrudePath,extrudeByPath=!1,uvgen=void 0!==options.UVGenerator?options.UVGenerator:THREE.ExtrudeGeometry.WorldUVGenerator;extrudePath&&(extrudePts=extrudePath.getSpacedPoints(steps),extrudeByPath=!0,bevelEnabled=!1,splineTube=void 0!==options.frames?options.frames:new THREE.TubeGeometry.FrenetFrames(extrudePath,steps,!1),binormal=new THREE.Vector3,normal=new THREE.Vector3,position2=new THREE.Vector3),bevelEnabled||(bevelSegments=0,bevelThickness=0,bevelSize=0);var ahole,h,hl,scope=this,shapesOffset=this.vertices.length,shapePoints=shape.extractPoints(curveSegments),vertices=shapePoints.shape,holes=shapePoints.holes,reverse=!THREE.ShapeUtils.isClockWise(vertices);if(reverse){for(vertices=vertices.reverse(),h=0,hl=holes.length;hl>h;h++)ahole=holes[h],THREE.ShapeUtils.isClockWise(ahole)&&(holes[h]=ahole.reverse());reverse=!1}var faces=THREE.ShapeUtils.triangulateShape(vertices,holes),contour=vertices;for(h=0,hl=holes.length;hl>h;h++)ahole=holes[h],vertices=vertices.concat(ahole);for(var b,bs,t,z,vert,face,vlen=vertices.length,flen=faces.length,contourMovements=[],i=0,il=contour.length,j=il-1,k=i+1;il>i;i++,j++,k++)j===il&&(j=0),k===il&&(k=0),contourMovements[i]=getBevelVec(contour[i],contour[j],contour[k]);var oneHoleMovements,holesMovements=[],verticesMovements=contourMovements.concat();for(h=0,hl=holes.length;hl>h;h++){for(ahole=holes[h],oneHoleMovements=[],i=0,il=ahole.length,j=il-1,k=i+1;il>i;i++,j++,k++)j===il&&(j=0),k===il&&(k=0),oneHoleMovements[i]=getBevelVec(ahole[i],ahole[j],ahole[k]);holesMovements.push(oneHoleMovements),verticesMovements=verticesMovements.concat(oneHoleMovements)}for(b=0;bevelSegments>b;b++){for(t=b/bevelSegments,z=bevelThickness*(1-t),bs=bevelSize*Math.sin(t*Math.PI/2),i=0,il=contour.length;il>i;i++)vert=scalePt2(contour[i],contourMovements[i],bs),v(vert.x,vert.y,-z);for(h=0,hl=holes.length;hl>h;h++)for(ahole=holes[h],oneHoleMovements=holesMovements[h],i=0,il=ahole.length;il>i;i++)vert=scalePt2(ahole[i],oneHoleMovements[i],bs),v(vert.x,vert.y,-z)}for(bs=bevelSize,i=0;vlen>i;i++)vert=bevelEnabled?scalePt2(vertices[i],verticesMovements[i],bs):vertices[i],extrudeByPath?(normal.copy(splineTube.normals[0]).multiplyScalar(vert.x),binormal.copy(splineTube.binormals[0]).multiplyScalar(vert.y),position2.copy(extrudePts[0]).add(normal).add(binormal),v(position2.x,position2.y,position2.z)):v(vert.x,vert.y,0);var s;for(s=1;steps>=s;s++)for(i=0;vlen>i;i++)vert=bevelEnabled?scalePt2(vertices[i],verticesMovements[i],bs):vertices[i],extrudeByPath?(normal.copy(splineTube.normals[s]).multiplyScalar(vert.x),binormal.copy(splineTube.binormals[s]).multiplyScalar(vert.y),position2.copy(extrudePts[s]).add(normal).add(binormal),v(position2.x,position2.y,position2.z)):v(vert.x,vert.y,amount/steps*s);for(b=bevelSegments-1;b>=0;b--){for(t=b/bevelSegments,z=bevelThickness*(1-t),bs=bevelSize*Math.sin(t*Math.PI/2),i=0,il=contour.length;il>i;i++)vert=scalePt2(contour[i],contourMovements[i],bs),v(vert.x,vert.y,amount+z);for(h=0,hl=holes.length;hl>h;h++)for(ahole=holes[h],oneHoleMovements=holesMovements[h],i=0,il=ahole.length;il>i;i++)vert=scalePt2(ahole[i],oneHoleMovements[i],bs),extrudeByPath?v(vert.x,vert.y+extrudePts[steps-1].y,extrudePts[steps-1].x+z):v(vert.x,vert.y,amount+z)}buildLidFaces(),buildSideFaces()},
+THREE.ExtrudeGeometry.WorldUVGenerator={generateTopUV:function(geometry,indexA,indexB,indexC){var vertices=geometry.vertices,a=vertices[indexA],b=vertices[indexB],c=vertices[indexC];return[new THREE.Vector2(a.x,a.y),new THREE.Vector2(b.x,b.y),new THREE.Vector2(c.x,c.y)]},generateSideWallUV:function(geometry,indexA,indexB,indexC,indexD){var vertices=geometry.vertices,a=vertices[indexA],b=vertices[indexB],c=vertices[indexC],d=vertices[indexD];return Math.abs(a.y-b.y)<.01?[new THREE.Vector2(a.x,1-a.z),new THREE.Vector2(b.x,1-b.z),new THREE.Vector2(c.x,1-c.z),new THREE.Vector2(d.x,1-d.z)]:[new THREE.Vector2(a.y,1-a.z),new THREE.Vector2(b.y,1-b.z),new THREE.Vector2(c.y,1-c.z),new THREE.Vector2(d.y,1-d.z)]}},THREE.ShapeGeometry=function(shapes,options){THREE.Geometry.call(this),this.type="ShapeGeometry",Array.isArray(shapes)===!1&&(shapes=[shapes]),this.addShapeList(shapes,options),this.computeFaceNormals()},THREE.ShapeGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.ShapeGeometry.prototype.constructor=THREE.ShapeGeometry,THREE.ShapeGeometry.prototype.addShapeList=function(shapes,options){for(var i=0,l=shapes.length;l>i;i++)this.addShape(shapes[i],options);return this},THREE.ShapeGeometry.prototype.addShape=function(shape,options){void 0===options&&(options={});var i,l,hole,curveSegments=void 0!==options.curveSegments?options.curveSegments:12,material=options.material,uvgen=void 0===options.UVGenerator?THREE.ExtrudeGeometry.WorldUVGenerator:options.UVGenerator,shapesOffset=this.vertices.length,shapePoints=shape.extractPoints(curveSegments),vertices=shapePoints.shape,holes=shapePoints.holes,reverse=!THREE.ShapeUtils.isClockWise(vertices);if(reverse){for(vertices=vertices.reverse(),i=0,l=holes.length;l>i;i++)hole=holes[i],THREE.ShapeUtils.isClockWise(hole)&&(holes[i]=hole.reverse());reverse=!1}var faces=THREE.ShapeUtils.triangulateShape(vertices,holes);for(i=0,l=holes.length;l>i;i++)hole=holes[i],vertices=vertices.concat(hole);var vert,face,vlen=vertices.length,flen=faces.length;for(i=0;vlen>i;i++)vert=vertices[i],this.vertices.push(new THREE.Vector3(vert.x,vert.y,0));for(i=0;flen>i;i++){face=faces[i];var a=face[0]+shapesOffset,b=face[1]+shapesOffset,c=face[2]+shapesOffset;this.faces.push(new THREE.Face3(a,b,c,null,null,material)),this.faceVertexUvs[0].push(uvgen.generateTopUV(this,a,b,c))}},THREE.LatheGeometry=function(points,segments,phiStart,phiLength){THREE.Geometry.call(this),this.type="LatheGeometry",this.parameters={points:points,segments:segments,phiStart:phiStart,phiLength:phiLength},segments=segments||12,phiStart=phiStart||0,phiLength=phiLength||2*Math.PI;for(var inversePointLength=1/(points.length-1),inverseSegments=1/segments,i=0,il=segments;il>=i;i++)for(var phi=phiStart+i*inverseSegments*phiLength,c=Math.cos(phi),s=Math.sin(phi),j=0,jl=points.length;jl>j;j++){var pt=points[j],vertex=new THREE.Vector3;vertex.x=c*pt.x-s*pt.y,vertex.y=s*pt.x+c*pt.y,vertex.z=pt.z,this.vertices.push(vertex)}for(var np=points.length,i=0,il=segments;il>i;i++)for(var j=0,jl=points.length-1;jl>j;j++){var base=j+np*i,a=base,b=base+np,c=base+1+np,d=base+1,u0=i*inverseSegments,v0=j*inversePointLength,u1=u0+inverseSegments,v1=v0+inversePointLength;this.faces.push(new THREE.Face3(a,b,d)),this.faceVertexUvs[0].push([new THREE.Vector2(u0,v0),new THREE.Vector2(u1,v0),new THREE.Vector2(u0,v1)]),this.faces.push(new THREE.Face3(b,c,d)),this.faceVertexUvs[0].push([new THREE.Vector2(u1,v0),new THREE.Vector2(u1,v1),new THREE.Vector2(u0,v1)])}this.mergeVertices(),this.computeFaceNormals(),this.computeVertexNormals()},THREE.LatheGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.LatheGeometry.prototype.constructor=THREE.LatheGeometry,THREE.PlaneGeometry=function(width,height,widthSegments,heightSegments){THREE.Geometry.call(this),this.type="PlaneGeometry",this.parameters={width:width,height:height,widthSegments:widthSegments,heightSegments:heightSegments},this.fromBufferGeometry(new THREE.PlaneBufferGeometry(width,height,widthSegments,heightSegments))},THREE.PlaneGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.PlaneGeometry.prototype.constructor=THREE.PlaneGeometry,THREE.PlaneGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.PlaneGeometry(parameters.width,parameters.height,parameters.widthSegments,parameters.heightSegments)},THREE.PlaneBufferGeometry=function(width,height,widthSegments,heightSegments){THREE.BufferGeometry.call(this),this.type="PlaneBufferGeometry",this.parameters={width:width,height:height,widthSegments:widthSegments,heightSegments:heightSegments};for(var width_half=width/2,height_half=height/2,gridX=Math.floor(widthSegments)||1,gridY=Math.floor(heightSegments)||1,gridX1=gridX+1,gridY1=gridY+1,segment_width=width/gridX,segment_height=height/gridY,vertices=new Float32Array(gridX1*gridY1*3),normals=new Float32Array(gridX1*gridY1*3),uvs=new Float32Array(gridX1*gridY1*2),offset=0,offset2=0,iy=0;gridY1>iy;iy++)for(var y=iy*segment_height-height_half,ix=0;gridX1>ix;ix++){var x=ix*segment_width-width_half;vertices[offset]=x,vertices[offset+1]=-y,normals[offset+2]=1,uvs[offset2]=ix/gridX,uvs[offset2+1]=1-iy/gridY,offset+=3,offset2+=2}offset=0;for(var indices=new(vertices.length/3>65535?Uint32Array:Uint16Array)(gridX*gridY*6),iy=0;gridY>iy;iy++)for(var ix=0;gridX>ix;ix++){var a=ix+gridX1*iy,b=ix+gridX1*(iy+1),c=ix+1+gridX1*(iy+1),d=ix+1+gridX1*iy;indices[offset]=a,indices[offset+1]=b,indices[offset+2]=d,indices[offset+3]=b,indices[offset+4]=c,indices[offset+5]=d,offset+=6}this.setIndex(new THREE.BufferAttribute(indices,1)),this.addAttribute("position",new THREE.BufferAttribute(vertices,3)),this.addAttribute("normal",new THREE.BufferAttribute(normals,3)),this.addAttribute("uv",new THREE.BufferAttribute(uvs,2))},THREE.PlaneBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.PlaneBufferGeometry.prototype.constructor=THREE.PlaneBufferGeometry,THREE.PlaneBufferGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.PlaneBufferGeometry(parameters.width,parameters.height,parameters.widthSegments,parameters.heightSegments)},THREE.RingGeometry=function(innerRadius,outerRadius,thetaSegments,phiSegments,thetaStart,thetaLength){THREE.Geometry.call(this),this.type="RingGeometry",this.parameters={innerRadius:innerRadius,outerRadius:outerRadius,thetaSegments:thetaSegments,phiSegments:phiSegments,thetaStart:thetaStart,thetaLength:thetaLength},innerRadius=innerRadius||0,outerRadius=outerRadius||50,thetaStart=void 0!==thetaStart?thetaStart:0,thetaLength=void 0!==thetaLength?thetaLength:2*Math.PI,thetaSegments=void 0!==thetaSegments?Math.max(3,thetaSegments):8,phiSegments=void 0!==phiSegments?Math.max(1,phiSegments):8;var i,o,uvs=[],radius=innerRadius,radiusStep=(outerRadius-innerRadius)/phiSegments;for(i=0;phiSegments+1>i;i++){for(o=0;thetaSegments+1>o;o++){var vertex=new THREE.Vector3,segment=thetaStart+o/thetaSegments*thetaLength;vertex.x=radius*Math.cos(segment),vertex.y=radius*Math.sin(segment),this.vertices.push(vertex),uvs.push(new THREE.Vector2((vertex.x/outerRadius+1)/2,(vertex.y/outerRadius+1)/2))}radius+=radiusStep}var n=new THREE.Vector3(0,0,1);for(i=0;phiSegments>i;i++){var thetaSegment=i*(thetaSegments+1);for(o=0;thetaSegments>o;o++){var segment=o+thetaSegment,v1=segment,v2=segment+thetaSegments+1,v3=segment+thetaSegments+2;this.faces.push(new THREE.Face3(v1,v2,v3,[n.clone(),n.clone(),n.clone()])),this.faceVertexUvs[0].push([uvs[v1].clone(),uvs[v2].clone(),uvs[v3].clone()]),v1=segment,v2=segment+thetaSegments+2,v3=segment+1,this.faces.push(new THREE.Face3(v1,v2,v3,[n.clone(),n.clone(),n.clone()])),this.faceVertexUvs[0].push([uvs[v1].clone(),uvs[v2].clone(),uvs[v3].clone()])}}this.computeFaceNormals(),this.boundingSphere=new THREE.Sphere(new THREE.Vector3,radius)},THREE.RingGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.RingGeometry.prototype.constructor=THREE.RingGeometry,THREE.RingGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.RingGeometry(parameters.innerRadius,parameters.outerRadius,parameters.thetaSegments,parameters.phiSegments,parameters.thetaStart,parameters.thetaLength)},THREE.SphereGeometry=function(radius,widthSegments,heightSegments,phiStart,phiLength,thetaStart,thetaLength){THREE.Geometry.call(this),this.type="SphereGeometry",this.parameters={radius:radius,widthSegments:widthSegments,heightSegments:heightSegments,phiStart:phiStart,phiLength:phiLength,thetaStart:thetaStart,thetaLength:thetaLength},this.fromBufferGeometry(new THREE.SphereBufferGeometry(radius,widthSegments,heightSegments,phiStart,phiLength,thetaStart,thetaLength))},THREE.SphereGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.SphereGeometry.prototype.constructor=THREE.SphereGeometry,THREE.SphereGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.SphereGeometry(parameters.radius,parameters.widthSegments,parameters.heightSegments,parameters.phiStart,parameters.phiLength,parameters.thetaStart,parameters.thetaLength)},THREE.SphereBufferGeometry=function(radius,widthSegments,heightSegments,phiStart,phiLength,thetaStart,thetaLength){THREE.BufferGeometry.call(this),this.type="SphereBufferGeometry",this.parameters={radius:radius,widthSegments:widthSegments,heightSegments:heightSegments,phiStart:phiStart,phiLength:phiLength,thetaStart:thetaStart,thetaLength:thetaLength},radius=radius||50,widthSegments=Math.max(3,Math.floor(widthSegments)||8),heightSegments=Math.max(2,Math.floor(heightSegments)||6),phiStart=void 0!==phiStart?phiStart:0,phiLength=void 0!==phiLength?phiLength:2*Math.PI,thetaStart=void 0!==thetaStart?thetaStart:0,thetaLength=void 0!==thetaLength?thetaLength:Math.PI;for(var thetaEnd=thetaStart+thetaLength,vertexCount=(widthSegments+1)*(heightSegments+1),positions=new THREE.BufferAttribute(new Float32Array(3*vertexCount),3),normals=new THREE.BufferAttribute(new Float32Array(3*vertexCount),3),uvs=new THREE.BufferAttribute(new Float32Array(2*vertexCount),2),index=0,vertices=[],normal=new THREE.Vector3,y=0;heightSegments>=y;y++){for(var verticesRow=[],v=y/heightSegments,x=0;widthSegments>=x;x++){var u=x/widthSegments,px=-radius*Math.cos(phiStart+u*phiLength)*Math.sin(thetaStart+v*thetaLength),py=radius*Math.cos(thetaStart+v*thetaLength),pz=radius*Math.sin(phiStart+u*phiLength)*Math.sin(thetaStart+v*thetaLength);normal.set(px,py,pz).normalize(),positions.setXYZ(index,px,py,pz),normals.setXYZ(index,normal.x,normal.y,normal.z),uvs.setXY(index,u,1-v),verticesRow.push(index),index++}vertices.push(verticesRow)}for(var indices=[],y=0;heightSegments>y;y++)for(var x=0;widthSegments>x;x++){var v1=vertices[y][x+1],v2=vertices[y][x],v3=vertices[y+1][x],v4=vertices[y+1][x+1];(0!==y||thetaStart>0)&&indices.push(v1,v2,v4),(y!==heightSegments-1||thetaEnd65535?THREE.Uint32Attribute:THREE.Uint16Attribute)(indices,1)),this.addAttribute("position",positions),this.addAttribute("normal",normals),this.addAttribute("uv",uvs),this.boundingSphere=new THREE.Sphere(new THREE.Vector3,radius)},THREE.SphereBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.SphereBufferGeometry.prototype.constructor=THREE.SphereBufferGeometry,THREE.SphereBufferGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.SphereBufferGeometry(parameters.radius,parameters.widthSegments,parameters.heightSegments,parameters.phiStart,parameters.phiLength,parameters.thetaStart,parameters.thetaLength)},THREE.TorusGeometry=function(radius,tube,radialSegments,tubularSegments,arc){THREE.Geometry.call(this),this.type="TorusGeometry",this.parameters={radius:radius,tube:tube,radialSegments:radialSegments,tubularSegments:tubularSegments,arc:arc},radius=radius||100,tube=tube||40,radialSegments=radialSegments||8,tubularSegments=tubularSegments||6,arc=arc||2*Math.PI;for(var center=new THREE.Vector3,uvs=[],normals=[],j=0;radialSegments>=j;j++)for(var i=0;tubularSegments>=i;i++){var u=i/tubularSegments*arc,v=j/radialSegments*Math.PI*2;center.x=radius*Math.cos(u),center.y=radius*Math.sin(u);var vertex=new THREE.Vector3;vertex.x=(radius+tube*Math.cos(v))*Math.cos(u),vertex.y=(radius+tube*Math.cos(v))*Math.sin(u),vertex.z=tube*Math.sin(v),this.vertices.push(vertex),uvs.push(new THREE.Vector2(i/tubularSegments,j/radialSegments)),normals.push(vertex.clone().sub(center).normalize())}for(var j=1;radialSegments>=j;j++)for(var i=1;tubularSegments>=i;i++){var a=(tubularSegments+1)*j+i-1,b=(tubularSegments+1)*(j-1)+i-1,c=(tubularSegments+1)*(j-1)+i,d=(tubularSegments+1)*j+i,face=new THREE.Face3(a,b,d,[normals[a].clone(),normals[b].clone(),normals[d].clone()]);this.faces.push(face),this.faceVertexUvs[0].push([uvs[a].clone(),uvs[b].clone(),uvs[d].clone()]),face=new THREE.Face3(b,c,d,[normals[b].clone(),normals[c].clone(),normals[d].clone()]),this.faces.push(face),this.faceVertexUvs[0].push([uvs[b].clone(),uvs[c].clone(),uvs[d].clone()])}this.computeFaceNormals()},THREE.TorusGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.TorusGeometry.prototype.constructor=THREE.TorusGeometry,THREE.TorusGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.TorusGeometry(parameters.radius,parameters.tube,parameters.radialSegments,parameters.tubularSegments,parameters.arc)},THREE.TorusKnotGeometry=function(radius,tube,radialSegments,tubularSegments,p,q,heightScale){function getPos(u,in_q,in_p,radius,heightScale){var cu=Math.cos(u),su=Math.sin(u),quOverP=in_q/in_p*u,cs=Math.cos(quOverP),tx=radius*(2+cs)*.5*cu,ty=radius*(2+cs)*su*.5,tz=heightScale*radius*Math.sin(quOverP)*.5;return new THREE.Vector3(tx,ty,tz)}THREE.Geometry.call(this),this.type="TorusKnotGeometry",this.parameters={radius:radius,tube:tube,radialSegments:radialSegments,tubularSegments:tubularSegments,p:p,q:q,heightScale:heightScale},radius=radius||100,tube=tube||40,radialSegments=radialSegments||64,tubularSegments=tubularSegments||8,p=p||2,q=q||3,heightScale=heightScale||1;for(var grid=new Array(radialSegments),tang=new THREE.Vector3,n=new THREE.Vector3,bitan=new THREE.Vector3,i=0;radialSegments>i;++i){grid[i]=new Array(tubularSegments);var u=i/radialSegments*2*p*Math.PI,p1=getPos(u,q,p,radius,heightScale),p2=getPos(u+.01,q,p,radius,heightScale);tang.subVectors(p2,p1),n.addVectors(p2,p1),bitan.crossVectors(tang,n),n.crossVectors(bitan,tang),bitan.normalize(),n.normalize();for(var j=0;tubularSegments>j;++j){var v=j/tubularSegments*2*Math.PI,cx=-tube*Math.cos(v),cy=tube*Math.sin(v),pos=new THREE.Vector3;pos.x=p1.x+cx*n.x+cy*bitan.x,pos.y=p1.y+cx*n.y+cy*bitan.y,pos.z=p1.z+cx*n.z+cy*bitan.z,grid[i][j]=this.vertices.push(pos)-1}}for(var i=0;radialSegments>i;++i)for(var j=0;tubularSegments>j;++j){var ip=(i+1)%radialSegments,jp=(j+1)%tubularSegments,a=grid[i][j],b=grid[ip][j],c=grid[ip][jp],d=grid[i][jp],uva=new THREE.Vector2(i/radialSegments,j/tubularSegments),uvb=new THREE.Vector2((i+1)/radialSegments,j/tubularSegments),uvc=new THREE.Vector2((i+1)/radialSegments,(j+1)/tubularSegments),uvd=new THREE.Vector2(i/radialSegments,(j+1)/tubularSegments);this.faces.push(new THREE.Face3(a,b,d)),this.faceVertexUvs[0].push([uva,uvb,uvd]),this.faces.push(new THREE.Face3(b,c,d)),this.faceVertexUvs[0].push([uvb.clone(),uvc,uvd.clone()])}this.computeFaceNormals(),this.computeVertexNormals()},THREE.TorusKnotGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.TorusKnotGeometry.prototype.constructor=THREE.TorusKnotGeometry,THREE.TorusKnotGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.TorusKnotGeometry(parameters.radius,parameters.tube,parameters.radialSegments,parameters.tubularSegments,parameters.p,parameters.q,parameters.heightScale)},THREE.TubeGeometry=function(path,segments,radius,radialSegments,closed,taper){function vert(x,y,z){return scope.vertices.push(new THREE.Vector3(x,y,z))-1}THREE.Geometry.call(this),this.type="TubeGeometry",this.parameters={path:path,segments:segments,radius:radius,radialSegments:radialSegments,closed:closed,taper:taper},segments=segments||64,radius=radius||1,radialSegments=radialSegments||8,closed=closed||!1,taper=taper||THREE.TubeGeometry.NoTaper;var tangent,normal,binormal,u,v,r,cx,cy,pos,i,j,ip,jp,a,b,c,d,uva,uvb,uvc,uvd,grid=[],scope=this,numpoints=segments+1,pos2=new THREE.Vector3,frames=new THREE.TubeGeometry.FrenetFrames(path,segments,closed),tangents=frames.tangents,normals=frames.normals,binormals=frames.binormals;for(this.tangents=tangents,this.normals=normals,this.binormals=binormals,i=0;numpoints>i;i++)for(grid[i]=[],u=i/(numpoints-1),pos=path.getPointAt(u),tangent=tangents[i],normal=normals[i],binormal=binormals[i],r=radius*taper(u),j=0;radialSegments>j;j++)v=j/radialSegments*2*Math.PI,cx=-r*Math.cos(v),cy=r*Math.sin(v),pos2.copy(pos),pos2.x+=cx*normal.x+cy*binormal.x,pos2.y+=cx*normal.y+cy*binormal.y,pos2.z+=cx*normal.z+cy*binormal.z,grid[i][j]=vert(pos2.x,pos2.y,pos2.z);for(i=0;segments>i;i++)for(j=0;radialSegments>j;j++)ip=closed?(i+1)%segments:i+1,jp=(j+1)%radialSegments,a=grid[i][j],b=grid[ip][j],c=grid[ip][jp],d=grid[i][jp],uva=new THREE.Vector2(i/segments,j/radialSegments),uvb=new THREE.Vector2((i+1)/segments,j/radialSegments),uvc=new THREE.Vector2((i+1)/segments,(j+1)/radialSegments),uvd=new THREE.Vector2(i/segments,(j+1)/radialSegments),this.faces.push(new THREE.Face3(a,b,d)),this.faceVertexUvs[0].push([uva,uvb,uvd]),this.faces.push(new THREE.Face3(b,c,d)),this.faceVertexUvs[0].push([uvb.clone(),uvc,uvd.clone()]);this.computeFaceNormals(),this.computeVertexNormals()},THREE.TubeGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.TubeGeometry.prototype.constructor=THREE.TubeGeometry,THREE.TubeGeometry.prototype.clone=function(){return new this.constructor(this.parameters.path,this.parameters.segments,this.parameters.radius,this.parameters.radialSegments,this.parameters.closed,this.parameters.taper)},THREE.TubeGeometry.NoTaper=function(u){return 1},THREE.TubeGeometry.SinusoidalTaper=function(u){return Math.sin(Math.PI*u)},THREE.TubeGeometry.FrenetFrames=function(path,segments,closed){function initialNormal3(){normals[0]=new THREE.Vector3,binormals[0]=new THREE.Vector3,smallest=Number.MAX_VALUE,tx=Math.abs(tangents[0].x),ty=Math.abs(tangents[0].y),tz=Math.abs(tangents[0].z),smallest>=tx&&(smallest=tx,normal.set(1,0,0)),smallest>=ty&&(smallest=ty,normal.set(0,1,0)),smallest>=tz&&normal.set(0,0,1),vec.crossVectors(tangents[0],normal).normalize(),normals[0].crossVectors(tangents[0],vec),binormals[0].crossVectors(tangents[0],normals[0])}var theta,smallest,tx,ty,tz,i,u,normal=new THREE.Vector3,tangents=[],normals=[],binormals=[],vec=new THREE.Vector3,mat=new THREE.Matrix4,numpoints=segments+1;for(this.tangents=tangents,this.normals=normals,this.binormals=binormals,i=0;numpoints>i;i++)u=i/(numpoints-1),tangents[i]=path.getTangentAt(u),tangents[i].normalize();for(initialNormal3(),i=1;numpoints>i;i++)normals[i]=normals[i-1].clone(),binormals[i]=binormals[i-1].clone(),vec.crossVectors(tangents[i-1],tangents[i]),vec.length()>Number.EPSILON&&(vec.normalize(),theta=Math.acos(THREE.Math.clamp(tangents[i-1].dot(tangents[i]),-1,1)),normals[i].applyMatrix4(mat.makeRotationAxis(vec,theta))),binormals[i].crossVectors(tangents[i],normals[i]);if(closed)for(theta=Math.acos(THREE.Math.clamp(normals[0].dot(normals[numpoints-1]),-1,1)),theta/=numpoints-1,tangents[0].dot(vec.crossVectors(normals[0],normals[numpoints-1]))>0&&(theta=-theta),i=1;numpoints>i;i++)normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i],theta*i)),binormals[i].crossVectors(tangents[i],normals[i])},THREE.PolyhedronGeometry=function(vertices,indices,radius,detail){function prepare(vector){var vertex=vector.normalize().clone();vertex.index=that.vertices.push(vertex)-1;var u=azimuth(vector)/2/Math.PI+.5,v=inclination(vector)/Math.PI+.5;return vertex.uv=new THREE.Vector2(u,1-v),vertex}function make(v1,v2,v3,materialIndex){var face=new THREE.Face3(v1.index,v2.index,v3.index,[v1.clone(),v2.clone(),v3.clone()],void 0,materialIndex);that.faces.push(face),centroid.copy(v1).add(v2).add(v3).divideScalar(3);var azi=azimuth(centroid);that.faceVertexUvs[0].push([correctUV(v1.uv,v1,azi),correctUV(v2.uv,v2,azi),correctUV(v3.uv,v3,azi)])}function subdivide(face,detail){for(var cols=Math.pow(2,detail),a=prepare(that.vertices[face.a]),b=prepare(that.vertices[face.b]),c=prepare(that.vertices[face.c]),v=[],materialIndex=face.materialIndex,i=0;cols>=i;i++){v[i]=[];for(var aj=prepare(a.clone().lerp(c,i/cols)),bj=prepare(b.clone().lerp(c,i/cols)),rows=cols-i,j=0;rows>=j;j++)0===j&&i===cols?v[i][j]=aj:v[i][j]=prepare(aj.clone().lerp(bj,j/rows))}for(var i=0;cols>i;i++)for(var j=0;2*(cols-i)-1>j;j++){var k=Math.floor(j/2);j%2===0?make(v[i][k+1],v[i+1][k],v[i][k],materialIndex):make(v[i][k+1],v[i+1][k+1],v[i+1][k],materialIndex)}}function azimuth(vector){return Math.atan2(vector.z,-vector.x)}function inclination(vector){return Math.atan2(-vector.y,Math.sqrt(vector.x*vector.x+vector.z*vector.z))}function correctUV(uv,vector,azimuth){return 0>azimuth&&1===uv.x&&(uv=new THREE.Vector2(uv.x-1,uv.y)),0===vector.x&&0===vector.z&&(uv=new THREE.Vector2(azimuth/2/Math.PI+.5,uv.y)),uv.clone()}THREE.Geometry.call(this),this.type="PolyhedronGeometry",this.parameters={vertices:vertices,indices:indices,radius:radius,detail:detail},radius=radius||1,detail=detail||0;for(var that=this,i=0,l=vertices.length;l>i;i+=3)prepare(new THREE.Vector3(vertices[i],vertices[i+1],vertices[i+2]));for(var p=this.vertices,faces=[],i=0,j=0,l=indices.length;l>i;i+=3,j++){var v1=p[indices[i]],v2=p[indices[i+1]],v3=p[indices[i+2]];faces[j]=new THREE.Face3(v1.index,v2.index,v3.index,[v1.clone(),v2.clone(),v3.clone()],void 0,j)}for(var centroid=new THREE.Vector3,i=0,l=faces.length;l>i;i++)subdivide(faces[i],detail);for(var i=0,l=this.faceVertexUvs[0].length;l>i;i++){var uvs=this.faceVertexUvs[0][i],x0=uvs[0].x,x1=uvs[1].x,x2=uvs[2].x,max=Math.max(x0,x1,x2),min=Math.min(x0,x1,x2);max>.9&&.1>min&&(.2>x0&&(uvs[0].x+=1),.2>x1&&(uvs[1].x+=1),.2>x2&&(uvs[2].x+=1))}for(var i=0,l=this.vertices.length;l>i;i++)this.vertices[i].multiplyScalar(radius);this.mergeVertices(),this.computeFaceNormals(),this.boundingSphere=new THREE.Sphere(new THREE.Vector3,radius)},THREE.PolyhedronGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.PolyhedronGeometry.prototype.constructor=THREE.PolyhedronGeometry,THREE.PolyhedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.PolyhedronGeometry(parameters.vertices,parameters.indices,parameters.radius,parameters.detail)},THREE.DodecahedronGeometry=function(radius,detail){var t=(1+Math.sqrt(5))/2,r=1/t,vertices=[-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,-r,-t,0,-r,t,0,r,-t,0,r,t,-r,-t,0,-r,t,0,r,-t,0,r,t,0,-t,0,-r,t,0,-r,-t,0,r,t,0,r],indices=[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];THREE.PolyhedronGeometry.call(this,vertices,indices,radius,detail),this.type="DodecahedronGeometry",this.parameters={radius:radius,detail:detail}},THREE.DodecahedronGeometry.prototype=Object.create(THREE.PolyhedronGeometry.prototype),THREE.DodecahedronGeometry.prototype.constructor=THREE.DodecahedronGeometry,THREE.DodecahedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.DodecahedronGeometry(parameters.radius,parameters.detail)},THREE.IcosahedronGeometry=function(radius,detail){var t=(1+Math.sqrt(5))/2,vertices=[-1,t,0,1,t,0,-1,-t,0,1,-t,0,0,-1,t,0,1,t,0,-1,-t,0,1,-t,t,0,-1,t,0,1,-t,0,-1,-t,0,1],indices=[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];THREE.PolyhedronGeometry.call(this,vertices,indices,radius,detail),this.type="IcosahedronGeometry",this.parameters={radius:radius,detail:detail}},THREE.IcosahedronGeometry.prototype=Object.create(THREE.PolyhedronGeometry.prototype),THREE.IcosahedronGeometry.prototype.constructor=THREE.IcosahedronGeometry,THREE.IcosahedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.IcosahedronGeometry(parameters.radius,parameters.detail)},THREE.OctahedronGeometry=function(radius,detail){var vertices=[1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1],indices=[0,2,4,0,4,3,0,3,5,0,5,2,1,2,5,1,5,3,1,3,4,1,4,2];THREE.PolyhedronGeometry.call(this,vertices,indices,radius,detail),this.type="OctahedronGeometry",this.parameters={radius:radius,detail:detail}},THREE.OctahedronGeometry.prototype=Object.create(THREE.PolyhedronGeometry.prototype),THREE.OctahedronGeometry.prototype.constructor=THREE.OctahedronGeometry,THREE.OctahedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.OctahedronGeometry(parameters.radius,parameters.detail)},THREE.TetrahedronGeometry=function(radius,detail){var vertices=[1,1,1,-1,-1,1,-1,1,-1,1,-1,-1],indices=[2,1,0,0,3,2,1,3,0,2,3,1];THREE.PolyhedronGeometry.call(this,vertices,indices,radius,detail),this.type="TetrahedronGeometry",this.parameters={radius:radius,detail:detail}},THREE.TetrahedronGeometry.prototype=Object.create(THREE.PolyhedronGeometry.prototype),THREE.TetrahedronGeometry.prototype.constructor=THREE.TetrahedronGeometry,THREE.TetrahedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.TetrahedronGeometry(parameters.radius,parameters.detail)},THREE.ParametricGeometry=function(func,slices,stacks){THREE.Geometry.call(this),this.type="ParametricGeometry",this.parameters={func:func,slices:slices,stacks:stacks};var i,j,p,u,v,verts=this.vertices,faces=this.faces,uvs=this.faceVertexUvs[0],sliceCount=slices+1;for(i=0;stacks>=i;i++)for(v=i/stacks,j=0;slices>=j;j++)u=j/slices,p=func(u,v),verts.push(p);var a,b,c,d,uva,uvb,uvc,uvd;for(i=0;stacks>i;i++)for(j=0;slices>j;j++)a=i*sliceCount+j,b=i*sliceCount+j+1,c=(i+1)*sliceCount+j+1,d=(i+1)*sliceCount+j,uva=new THREE.Vector2(j/slices,i/stacks),uvb=new THREE.Vector2((j+1)/slices,i/stacks),uvc=new THREE.Vector2((j+1)/slices,(i+1)/stacks),uvd=new THREE.Vector2(j/slices,(i+1)/stacks),faces.push(new THREE.Face3(a,b,d)),uvs.push([uva,uvb,uvd]),faces.push(new THREE.Face3(b,c,d)),uvs.push([uvb.clone(),uvc,uvd.clone()]);this.computeFaceNormals(),this.computeVertexNormals()},THREE.ParametricGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.ParametricGeometry.prototype.constructor=THREE.ParametricGeometry,THREE.WireframeGeometry=function(geometry){function sortFunction(a,b){return a-b}THREE.BufferGeometry.call(this);var edge=[0,0],hash={},keys=["a","b","c"];if(geometry instanceof THREE.Geometry){for(var vertices=geometry.vertices,faces=geometry.faces,numEdges=0,edges=new Uint32Array(6*faces.length),i=0,l=faces.length;l>i;i++)for(var face=faces[i],j=0;3>j;j++){edge[0]=face[keys[j]],edge[1]=face[keys[(j+1)%3]],edge.sort(sortFunction);var key=edge.toString();void 0===hash[key]&&(edges[2*numEdges]=edge[0],edges[2*numEdges+1]=edge[1],hash[key]=!0,numEdges++)}for(var coords=new Float32Array(2*numEdges*3),i=0,l=numEdges;l>i;i++)for(var j=0;2>j;j++){var vertex=vertices[edges[2*i+j]],index=6*i+3*j;coords[index+0]=vertex.x,coords[index+1]=vertex.y,coords[index+2]=vertex.z}this.addAttribute("position",new THREE.BufferAttribute(coords,3))}else if(geometry instanceof THREE.BufferGeometry)if(null!==geometry.index){var indices=geometry.index.array,vertices=geometry.attributes.position,drawcalls=geometry.drawcalls,numEdges=0;0===drawcalls.length&&geometry.addGroup(0,indices.length);for(var edges=new Uint32Array(2*indices.length),o=0,ol=drawcalls.length;ol>o;++o)for(var drawcall=drawcalls[o],start=drawcall.start,count=drawcall.count,i=start,il=start+count;il>i;i+=3)for(var j=0;3>j;j++){edge[0]=indices[i+j],edge[1]=indices[i+(j+1)%3],edge.sort(sortFunction);var key=edge.toString();void 0===hash[key]&&(edges[2*numEdges]=edge[0],edges[2*numEdges+1]=edge[1],hash[key]=!0,numEdges++)}for(var coords=new Float32Array(2*numEdges*3),i=0,l=numEdges;l>i;i++)for(var j=0;2>j;j++){var index=6*i+3*j,index2=edges[2*i+j];coords[index+0]=vertices.getX(index2),coords[index+1]=vertices.getY(index2),coords[index+2]=vertices.getZ(index2)}this.addAttribute("position",new THREE.BufferAttribute(coords,3))}else{for(var vertices=geometry.attributes.position.array,numEdges=vertices.length/3,numTris=numEdges/3,coords=new Float32Array(2*numEdges*3),i=0,l=numTris;l>i;i++)for(var j=0;3>j;j++){var index=18*i+6*j,index1=9*i+3*j;coords[index+0]=vertices[index1],coords[index+1]=vertices[index1+1],coords[index+2]=vertices[index1+2];var index2=9*i+3*((j+1)%3);coords[index+3]=vertices[index2],coords[index+4]=vertices[index2+1],coords[index+5]=vertices[index2+2]}this.addAttribute("position",new THREE.BufferAttribute(coords,3))}},THREE.WireframeGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.WireframeGeometry.prototype.constructor=THREE.WireframeGeometry,THREE.AxisHelper=function(size){size=size||1;var vertices=new Float32Array([0,0,0,size,0,0,0,0,0,0,size,0,0,0,0,0,0,size]),colors=new Float32Array([1,0,0,1,.6,0,0,1,0,.6,1,0,0,0,1,0,.6,1]),geometry=new THREE.BufferGeometry;geometry.addAttribute("position",new THREE.BufferAttribute(vertices,3)),geometry.addAttribute("color",new THREE.BufferAttribute(colors,3));var material=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});THREE.LineSegments.call(this,geometry,material)},THREE.AxisHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.AxisHelper.prototype.constructor=THREE.AxisHelper,THREE.ArrowHelper=function(){var lineGeometry=new THREE.Geometry;lineGeometry.vertices.push(new THREE.Vector3(0,0,0),new THREE.Vector3(0,1,0));var coneGeometry=new THREE.CylinderGeometry(0,.5,1,5,1);return coneGeometry.translate(0,-.5,0),function(dir,origin,length,color,headLength,headWidth){THREE.Object3D.call(this),void 0===color&&(color=16776960),void 0===length&&(length=1),void 0===headLength&&(headLength=.2*length),void 0===headWidth&&(headWidth=.2*headLength),this.position.copy(origin),length>headLength&&(this.line=new THREE.Line(lineGeometry,new THREE.LineBasicMaterial({color:color})),this.line.matrixAutoUpdate=!1,this.add(this.line)),this.cone=new THREE.Mesh(coneGeometry,new THREE.MeshBasicMaterial({color:color})),this.cone.matrixAutoUpdate=!1,this.add(this.cone),this.setDirection(dir),this.setLength(length,headLength,headWidth)}}(),THREE.ArrowHelper.prototype=Object.create(THREE.Object3D.prototype),THREE.ArrowHelper.prototype.constructor=THREE.ArrowHelper,THREE.ArrowHelper.prototype.setDirection=function(){var radians,axis=new THREE.Vector3;return function(dir){dir.y>.99999?this.quaternion.set(0,0,0,1):dir.y<-.99999?this.quaternion.set(1,0,0,0):(axis.set(dir.z,0,-dir.x).normalize(),radians=Math.acos(dir.y),this.quaternion.setFromAxisAngle(axis,radians))}}(),THREE.ArrowHelper.prototype.setLength=function(length,headLength,headWidth){void 0===headLength&&(headLength=.2*length),void 0===headWidth&&(headWidth=.2*headLength),length>headLength&&(this.line.scale.set(1,length-headLength,1),this.line.updateMatrix()),this.cone.scale.set(headWidth,headLength,headWidth),this.cone.position.y=length,this.cone.updateMatrix()},THREE.ArrowHelper.prototype.setColor=function(color){void 0!==this.line&&this.line.material.color.set(color),this.cone.material.color.set(color)},THREE.BoxHelper=function(object){var indices=new Uint16Array([0,1,1,2,2,3,3,0,4,5,5,6,6,7,7,4,0,4,1,5,2,6,3,7]),positions=new Float32Array(24),geometry=new THREE.BufferGeometry;geometry.setIndex(new THREE.BufferAttribute(indices,1)),geometry.addAttribute("position",new THREE.BufferAttribute(positions,3)),THREE.LineSegments.call(this,geometry,new THREE.LineBasicMaterial({color:16776960})),void 0!==object&&this.update(object)},THREE.BoxHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.BoxHelper.prototype.constructor=THREE.BoxHelper,
+THREE.BoxHelper.prototype.update=function(){var box=new THREE.Box3;return function(object){if(box.setFromObject(object),!box.empty()){var min=box.min,max=box.max,position=this.geometry.attributes.position,array=position.array;array[0]=max.x,array[1]=max.y,array[2]=max.z,array[3]=min.x,array[4]=max.y,array[5]=max.z,array[6]=min.x,array[7]=min.y,array[8]=max.z,array[9]=max.x,array[10]=min.y,array[11]=max.z,array[12]=max.x,array[13]=max.y,array[14]=min.z,array[15]=min.x,array[16]=max.y,array[17]=min.z,array[18]=min.x,array[19]=min.y,array[20]=min.z,array[21]=max.x,array[22]=min.y,array[23]=min.z,position.needsUpdate=!0,this.geometry.computeBoundingSphere()}}}(),THREE.BoundingBoxHelper=function(object,hex){var color=void 0!==hex?hex:8947848;this.object=object,this.box=new THREE.Box3,THREE.Mesh.call(this,new THREE.BoxGeometry(1,1,1),new THREE.MeshBasicMaterial({color:color,wireframe:!0}))},THREE.BoundingBoxHelper.prototype=Object.create(THREE.Mesh.prototype),THREE.BoundingBoxHelper.prototype.constructor=THREE.BoundingBoxHelper,THREE.BoundingBoxHelper.prototype.update=function(){this.box.setFromObject(this.object),this.box.size(this.scale),this.box.center(this.position)},THREE.CameraHelper=function(camera){function addLine(a,b,hex){addPoint(a,hex),addPoint(b,hex)}function addPoint(id,hex){geometry.vertices.push(new THREE.Vector3),geometry.colors.push(new THREE.Color(hex)),void 0===pointMap[id]&&(pointMap[id]=[]),pointMap[id].push(geometry.vertices.length-1)}var geometry=new THREE.Geometry,material=new THREE.LineBasicMaterial({color:16777215,vertexColors:THREE.FaceColors}),pointMap={},hexFrustum=16755200,hexCone=16711680,hexUp=43775,hexTarget=16777215,hexCross=3355443;addLine("n1","n2",hexFrustum),addLine("n2","n4",hexFrustum),addLine("n4","n3",hexFrustum),addLine("n3","n1",hexFrustum),addLine("f1","f2",hexFrustum),addLine("f2","f4",hexFrustum),addLine("f4","f3",hexFrustum),addLine("f3","f1",hexFrustum),addLine("n1","f1",hexFrustum),addLine("n2","f2",hexFrustum),addLine("n3","f3",hexFrustum),addLine("n4","f4",hexFrustum),addLine("p","n1",hexCone),addLine("p","n2",hexCone),addLine("p","n3",hexCone),addLine("p","n4",hexCone),addLine("u1","u2",hexUp),addLine("u2","u3",hexUp),addLine("u3","u1",hexUp),addLine("c","t",hexTarget),addLine("p","c",hexCross),addLine("cn1","cn2",hexCross),addLine("cn3","cn4",hexCross),addLine("cf1","cf2",hexCross),addLine("cf3","cf4",hexCross),THREE.LineSegments.call(this,geometry,material),this.camera=camera,this.camera.updateProjectionMatrix(),this.matrix=camera.matrixWorld,this.matrixAutoUpdate=!1,this.pointMap=pointMap,this.update()},THREE.CameraHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.CameraHelper.prototype.constructor=THREE.CameraHelper,THREE.CameraHelper.prototype.update=function(){function setPoint(point,x,y,z){vector.set(x,y,z).unproject(camera);var points=pointMap[point];if(void 0!==points)for(var i=0,il=points.length;il>i;i++)geometry.vertices[points[i]].copy(vector)}var geometry,pointMap,vector=new THREE.Vector3,camera=new THREE.Camera;return function(){geometry=this.geometry,pointMap=this.pointMap;var w=1,h=1;camera.projectionMatrix.copy(this.camera.projectionMatrix),setPoint("c",0,0,-1),setPoint("t",0,0,1),setPoint("n1",-w,-h,-1),setPoint("n2",w,-h,-1),setPoint("n3",-w,h,-1),setPoint("n4",w,h,-1),setPoint("f1",-w,-h,1),setPoint("f2",w,-h,1),setPoint("f3",-w,h,1),setPoint("f4",w,h,1),setPoint("u1",.7*w,1.1*h,-1),setPoint("u2",.7*-w,1.1*h,-1),setPoint("u3",0,2*h,-1),setPoint("cf1",-w,0,1),setPoint("cf2",w,0,1),setPoint("cf3",0,-h,1),setPoint("cf4",0,h,1),setPoint("cn1",-w,0,-1),setPoint("cn2",w,0,-1),setPoint("cn3",0,-h,-1),setPoint("cn4",0,h,-1),geometry.verticesNeedUpdate=!0}}(),THREE.DirectionalLightHelper=function(light,size){THREE.Object3D.call(this),this.light=light,this.light.updateMatrixWorld(),this.matrix=light.matrixWorld,this.matrixAutoUpdate=!1,size=size||1;var geometry=new THREE.Geometry;geometry.vertices.push(new THREE.Vector3(-size,size,0),new THREE.Vector3(size,size,0),new THREE.Vector3(size,-size,0),new THREE.Vector3(-size,-size,0),new THREE.Vector3(-size,size,0));var material=new THREE.LineBasicMaterial({fog:!1});material.color.copy(this.light.color).multiplyScalar(this.light.intensity),this.lightPlane=new THREE.Line(geometry,material),this.add(this.lightPlane),geometry=new THREE.Geometry,geometry.vertices.push(new THREE.Vector3,new THREE.Vector3),material=new THREE.LineBasicMaterial({fog:!1}),material.color.copy(this.light.color).multiplyScalar(this.light.intensity),this.targetLine=new THREE.Line(geometry,material),this.add(this.targetLine),this.update()},THREE.DirectionalLightHelper.prototype=Object.create(THREE.Object3D.prototype),THREE.DirectionalLightHelper.prototype.constructor=THREE.DirectionalLightHelper,THREE.DirectionalLightHelper.prototype.dispose=function(){this.lightPlane.geometry.dispose(),this.lightPlane.material.dispose(),this.targetLine.geometry.dispose(),this.targetLine.material.dispose()},THREE.DirectionalLightHelper.prototype.update=function(){var v1=new THREE.Vector3,v2=new THREE.Vector3,v3=new THREE.Vector3;return function(){v1.setFromMatrixPosition(this.light.matrixWorld),v2.setFromMatrixPosition(this.light.target.matrixWorld),v3.subVectors(v2,v1),this.lightPlane.lookAt(v3),this.lightPlane.material.color.copy(this.light.color).multiplyScalar(this.light.intensity),this.targetLine.geometry.vertices[1].copy(v3),this.targetLine.geometry.verticesNeedUpdate=!0,this.targetLine.material.color.copy(this.lightPlane.material.color)}}(),THREE.EdgesHelper=function(object,hex,thresholdAngle){var color=void 0!==hex?hex:16777215;THREE.LineSegments.call(this,new THREE.EdgesGeometry(object.geometry,thresholdAngle),new THREE.LineBasicMaterial({color:color})),this.matrix=object.matrixWorld,this.matrixAutoUpdate=!1},THREE.EdgesHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.EdgesHelper.prototype.constructor=THREE.EdgesHelper,THREE.FaceNormalsHelper=function(object,size,hex,linewidth){this.object=object,this.size=void 0!==size?size:1;var color=void 0!==hex?hex:16776960,width=void 0!==linewidth?linewidth:1,nNormals=0,objGeometry=this.object.geometry;objGeometry instanceof THREE.Geometry?nNormals=objGeometry.faces.length:console.warn("THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.");var geometry=new THREE.BufferGeometry,positions=new THREE.Float32Attribute(2*nNormals*3,3);geometry.addAttribute("position",positions),THREE.LineSegments.call(this,geometry,new THREE.LineBasicMaterial({color:color,linewidth:width})),this.matrixAutoUpdate=!1,this.update()},THREE.FaceNormalsHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.FaceNormalsHelper.prototype.constructor=THREE.FaceNormalsHelper,THREE.FaceNormalsHelper.prototype.update=function(){var v1=new THREE.Vector3,v2=new THREE.Vector3,normalMatrix=new THREE.Matrix3;return function(){this.object.updateMatrixWorld(!0),normalMatrix.getNormalMatrix(this.object.matrixWorld);for(var matrixWorld=this.object.matrixWorld,position=this.geometry.attributes.position,objGeometry=this.object.geometry,vertices=objGeometry.vertices,faces=objGeometry.faces,idx=0,i=0,l=faces.length;l>i;i++){var face=faces[i],normal=face.normal;v1.copy(vertices[face.a]).add(vertices[face.b]).add(vertices[face.c]).divideScalar(3).applyMatrix4(matrixWorld),v2.copy(normal).applyMatrix3(normalMatrix).normalize().multiplyScalar(this.size).add(v1),position.setXYZ(idx,v1.x,v1.y,v1.z),idx+=1,position.setXYZ(idx,v2.x,v2.y,v2.z),idx+=1}return position.needsUpdate=!0,this}}(),THREE.GridHelper=function(size,step){var geometry=new THREE.Geometry,material=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});this.color1=new THREE.Color(4473924),this.color2=new THREE.Color(8947848);for(var i=-size;size>=i;i+=step){geometry.vertices.push(new THREE.Vector3(-size,0,i),new THREE.Vector3(size,0,i),new THREE.Vector3(i,0,-size),new THREE.Vector3(i,0,size));var color=0===i?this.color1:this.color2;geometry.colors.push(color,color,color,color)}THREE.LineSegments.call(this,geometry,material)},THREE.GridHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.GridHelper.prototype.constructor=THREE.GridHelper,THREE.GridHelper.prototype.setColors=function(colorCenterLine,colorGrid){this.color1.set(colorCenterLine),this.color2.set(colorGrid),this.geometry.colorsNeedUpdate=!0},THREE.HemisphereLightHelper=function(light,sphereSize){THREE.Object3D.call(this),this.light=light,this.light.updateMatrixWorld(),this.matrix=light.matrixWorld,this.matrixAutoUpdate=!1,this.colors=[new THREE.Color,new THREE.Color];var geometry=new THREE.SphereGeometry(sphereSize,4,2);geometry.rotateX(-Math.PI/2);for(var i=0,il=8;il>i;i++)geometry.faces[i].color=this.colors[4>i?0:1];var material=new THREE.MeshBasicMaterial({vertexColors:THREE.FaceColors,wireframe:!0});this.lightSphere=new THREE.Mesh(geometry,material),this.add(this.lightSphere),this.update()},THREE.HemisphereLightHelper.prototype=Object.create(THREE.Object3D.prototype),THREE.HemisphereLightHelper.prototype.constructor=THREE.HemisphereLightHelper,THREE.HemisphereLightHelper.prototype.dispose=function(){this.lightSphere.geometry.dispose(),this.lightSphere.material.dispose()},THREE.HemisphereLightHelper.prototype.update=function(){var vector=new THREE.Vector3;return function(){this.colors[0].copy(this.light.color).multiplyScalar(this.light.intensity),this.colors[1].copy(this.light.groundColor).multiplyScalar(this.light.intensity),this.lightSphere.lookAt(vector.setFromMatrixPosition(this.light.matrixWorld).negate()),this.lightSphere.geometry.colorsNeedUpdate=!0}}(),THREE.PointLightHelper=function(light,sphereSize){this.light=light,this.light.updateMatrixWorld();var geometry=new THREE.SphereGeometry(sphereSize,4,2),material=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});material.color.copy(this.light.color).multiplyScalar(this.light.intensity),THREE.Mesh.call(this,geometry,material),this.matrix=this.light.matrixWorld,this.matrixAutoUpdate=!1},THREE.PointLightHelper.prototype=Object.create(THREE.Mesh.prototype),THREE.PointLightHelper.prototype.constructor=THREE.PointLightHelper,THREE.PointLightHelper.prototype.dispose=function(){this.geometry.dispose(),this.material.dispose()},THREE.PointLightHelper.prototype.update=function(){this.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)},THREE.SkeletonHelper=function(object){this.bones=this.getBoneList(object);for(var geometry=new THREE.Geometry,i=0;ii;i++)for(var face=faces[i],j=0,jl=face.vertexNormals.length;jl>j;j++){var vertex=vertices[face[keys[j]]],normal=face.vertexNormals[j];v1.copy(vertex).applyMatrix4(matrixWorld),v2.copy(normal).applyMatrix3(normalMatrix).normalize().multiplyScalar(this.size).add(v1),position.setXYZ(idx,v1.x,v1.y,v1.z),idx+=1,position.setXYZ(idx,v2.x,v2.y,v2.z),idx+=1}else if(objGeometry instanceof THREE.BufferGeometry)for(var objPos=objGeometry.attributes.position,objNorm=objGeometry.attributes.normal,idx=0,j=0,jl=objPos.count;jl>j;j++)v1.set(objPos.getX(j),objPos.getY(j),objPos.getZ(j)).applyMatrix4(matrixWorld),v2.set(objNorm.getX(j),objNorm.getY(j),objNorm.getZ(j)),v2.applyMatrix3(normalMatrix).normalize().multiplyScalar(this.size).add(v1),position.setXYZ(idx,v1.x,v1.y,v1.z),idx+=1,position.setXYZ(idx,v2.x,v2.y,v2.z),idx+=1;return position.needsUpdate=!0,this}}(),THREE.WireframeHelper=function(object,hex){var color=void 0!==hex?hex:16777215;THREE.LineSegments.call(this,new THREE.WireframeGeometry(object.geometry),new THREE.LineBasicMaterial({color:color})),this.matrix=object.matrixWorld,this.matrixAutoUpdate=!1},THREE.WireframeHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.WireframeHelper.prototype.constructor=THREE.WireframeHelper,THREE.ImmediateRenderObject=function(material){THREE.Object3D.call(this),this.material=material,this.render=function(renderCallback){}},THREE.ImmediateRenderObject.prototype=Object.create(THREE.Object3D.prototype),THREE.ImmediateRenderObject.prototype.constructor=THREE.ImmediateRenderObject,THREE.MorphBlendMesh=function(geometry,material){THREE.Mesh.call(this,geometry,material),this.animationsMap={},this.animationsList=[];var numFrames=this.geometry.morphTargets.length,name="__default",startFrame=0,endFrame=numFrames-1,fps=numFrames/1;this.createAnimation(name,startFrame,endFrame,fps),this.setAnimationWeight(name,1)},THREE.MorphBlendMesh.prototype=Object.create(THREE.Mesh.prototype),THREE.MorphBlendMesh.prototype.constructor=THREE.MorphBlendMesh,THREE.MorphBlendMesh.prototype.createAnimation=function(name,start,end,fps){var animation={start:start,end:end,length:end-start+1,fps:fps,duration:(end-start)/fps,lastFrame:0,currentFrame:0,active:!1,time:0,direction:1,weight:1,directionBackwards:!1,mirroredLoop:!1};this.animationsMap[name]=animation,this.animationsList.push(animation)},THREE.MorphBlendMesh.prototype.autoCreateAnimations=function(fps){for(var firstAnimation,pattern=/([a-z]+)_?(\d+)/,frameRanges={},geometry=this.geometry,i=0,il=geometry.morphTargets.length;il>i;i++){var morph=geometry.morphTargets[i],chunks=morph.name.match(pattern);if(chunks&&chunks.length>1){var name=chunks[1];frameRanges[name]||(frameRanges[name]={start:1/0,end:-(1/0)});var range=frameRanges[name];irange.end&&(range.end=i),firstAnimation||(firstAnimation=name)}}for(var name in frameRanges){var range=frameRanges[name];this.createAnimation(name,range.start,range.end,fps)}this.firstAnimation=firstAnimation},THREE.MorphBlendMesh.prototype.setAnimationDirectionForward=function(name){var animation=this.animationsMap[name];animation&&(animation.direction=1,animation.directionBackwards=!1)},THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward=function(name){var animation=this.animationsMap[name];animation&&(animation.direction=-1,animation.directionBackwards=!0)},THREE.MorphBlendMesh.prototype.setAnimationFPS=function(name,fps){var animation=this.animationsMap[name];animation&&(animation.fps=fps,animation.duration=(animation.end-animation.start)/animation.fps)},THREE.MorphBlendMesh.prototype.setAnimationDuration=function(name,duration){var animation=this.animationsMap[name];animation&&(animation.duration=duration,animation.fps=(animation.end-animation.start)/animation.duration)},THREE.MorphBlendMesh.prototype.setAnimationWeight=function(name,weight){var animation=this.animationsMap[name];animation&&(animation.weight=weight)},THREE.MorphBlendMesh.prototype.setAnimationTime=function(name,time){var animation=this.animationsMap[name];animation&&(animation.time=time)},THREE.MorphBlendMesh.prototype.getAnimationTime=function(name){var time=0,animation=this.animationsMap[name];return animation&&(time=animation.time),time},THREE.MorphBlendMesh.prototype.getAnimationDuration=function(name){var duration=-1,animation=this.animationsMap[name];return animation&&(duration=animation.duration),duration},THREE.MorphBlendMesh.prototype.playAnimation=function(name){var animation=this.animationsMap[name];animation?(animation.time=0,animation.active=!0):console.warn("THREE.MorphBlendMesh: animation["+name+"] undefined in .playAnimation()")},THREE.MorphBlendMesh.prototype.stopAnimation=function(name){var animation=this.animationsMap[name];animation&&(animation.active=!1)},THREE.MorphBlendMesh.prototype.update=function(delta){for(var i=0,il=this.animationsList.length;il>i;i++){var animation=this.animationsList[i];if(animation.active){var frameTime=animation.duration/animation.length;animation.time+=animation.direction*delta,animation.mirroredLoop?(animation.time>animation.duration||animation.time<0)&&(animation.direction*=-1,animation.time>animation.duration&&(animation.time=animation.duration,animation.directionBackwards=!0),animation.time<0&&(animation.time=0,animation.directionBackwards=!1)):(animation.time=animation.time%animation.duration,animation.time<0&&(animation.time+=animation.duration));var keyframe=animation.start+THREE.Math.clamp(Math.floor(animation.time/frameTime),0,animation.length-1),weight=animation.weight;keyframe!==animation.currentFrame&&(this.morphTargetInfluences[animation.lastFrame]=0,this.morphTargetInfluences[animation.currentFrame]=1*weight,this.morphTargetInfluences[keyframe]=0,animation.lastFrame=animation.currentFrame,animation.currentFrame=keyframe);var mix=animation.time%frameTime/frameTime;animation.directionBackwards&&(mix=1-mix),animation.currentFrame!==animation.lastFrame?(this.morphTargetInfluences[animation.currentFrame]=mix*weight,this.morphTargetInfluences[animation.lastFrame]=(1-mix)*weight):this.morphTargetInfluences[animation.currentFrame]=weight}}},"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=THREE),exports.THREE=THREE):this.THREE=THREE},{}],6:[function(_dereq_,module,exports){!function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof _dereq_&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof _dereq_&&_dereq_,o=0;o 0.0 && abs(vUV.x - 0.5) < .001) {","} else if (a.x < 0.0 || a.x > 1.0 || a.y < 0.0 || a.y > 1.0) {","gl_FragColor = backgroundColor;","} else {","gl_FragColor = texture2D(texture, vec2(a.x * 0.5 + (vUV.x < 0.5 ? 0.0 : 0.5), a.y));","}","}"].join("\n")};module.exports=BarrelDistortionFragment},{}],6:[function(_dereq_,module,exports){function Distortion(coefficients){this.coefficients=coefficients}Distortion.prototype.distortInverse=function(radius){for(var r0=radius/.9,r1=.9*radius,dr0=radius-this.distort(r0);Math.abs(r1-r0)>1e-4;){var dr1=radius-this.distort(r1),r2=r1-dr1*((r1-r0)/(dr1-dr0));r0=r1,r1=r2,dr0=dr1}return r1},Distortion.prototype.distort=function(radius){return radius*this.distortionFactor_(radius)},Distortion.prototype.distortionFactor_=function(radius){for(var result=1,rFactor=1,rSquared=radius*radius,i=0;i=200&&xhr.status<=299?(console.log("Successfully loaded online DPDB."),obj.dpdb=JSON.parse(xhr.response),obj.recalculateDeviceParams_()):console.error("Error loading online DPDB!")}),xhr.send()}}function DeviceParams(params){this.xdpi=params.xdpi,this.ydpi=params.ydpi,this.bevelMm=params.bevelMm}var DPDB_CACHE=_dereq_("./dpdb-cache.js"),Util=_dereq_("./util.js"),ONLINE_DPDB_URL="https://storage.googleapis.com/cardboard-dpdb/dpdb.json";Dpdb.prototype.getDeviceParams=function(){return this.deviceParams},Dpdb.prototype.recalculateDeviceParams_=function(){console.log("Recalculating device params.");var newDeviceParams=this.calcDeviceParams_();console.log("New device parameters:"),console.log(newDeviceParams),newDeviceParams?(this.deviceParams=newDeviceParams,this.onDeviceParamsUpdated&&this.onDeviceParamsUpdated(this.deviceParams)):console.warn("Failed to recalculate device parameters.")},Dpdb.prototype.calcDeviceParams_=function(){var db=this.dpdb;if(!db)return console.error("DPDB not available."),null;if(1!=db.format)return console.error("DPDB has unexpected format version."),null;if(!db.devices||!db.devices.length)return console.error("DPDB does not have a devices section."),null;var userAgent=navigator.userAgent||navigator.vendor||window.opera,width=Util.getScreenWidth(),height=Util.getScreenHeight();if(console.log("User agent: "+userAgent),console.log("Pixel width: "+width),console.log("Pixel height: "+height),!db.devices)return console.error("DPDB has no devices section."),null;for(var i=0;i %s",this.mode,mode),this.mode=mode,this.button.setMode(mode,this.isVRCompatible),this.mode==Modes.VR&&Util.isLandscapeMode()&&Util.isMobile()?this.rotateInstructions.showTemporarily(3e3):this.updateRotateInstructions_(),this.viewerSelector.hide(),this.emit("modechange",mode,oldMode),this.isTouchPannerEnabled&&(this.mode==Modes.VR?WebVRConfig.TOUCH_PANNER_DISABLED=!0:WebVRConfig.TOUCH_PANNER_DISABLED=!1),void(this.mode==Modes.VR&&this.setHMDVRDeviceParams_(this.getViewer())))},WebVRManager.prototype.onFSClick_=function(){switch(this.mode){case Modes.NORMAL:if(Util.isIOS()&&Util.isIFrame()){var url=window.location.href;return url=Util.appendQueryParameter(url,"no_fullscreen","true"),url=Util.appendQueryParameter(url,"start_mode",Modes.MAGIC_WINDOW),void(top.location.href=url)}this.normalToMagicWindow_(),this.setMode_(Modes.MAGIC_WINDOW);break;case Modes.MAGIC_WINDOW:this.isFullscreenDisabled?window.history.back():(this.anyModeToNormal_(),this.setMode_(Modes.NORMAL))}},WebVRManager.prototype.onVRClick_=function(){if(this.mode==Modes.NORMAL&&Util.isIOS()&&Util.isIFrame()){var url=window.location.href;return url=Util.appendQueryParameter(url,"no_fullscreen","true"),url=Util.appendQueryParameter(url,"start_mode",Modes.VR),void(top.location.href=url)}this.anyModeToVR_(),this.setMode_(Modes.VR)},WebVRManager.prototype.onBackClick_=function(){this.isFullscreenDisabled?window.history.back():(this.anyModeToNormal_(),this.setMode_(Modes.NORMAL))},WebVRManager.prototype.onSettingsClick_=function(){this.viewerSelector.show()},WebVRManager.prototype.normalToMagicWindow_=function(){this.requestFullscreen_(),this.wakelock.request()},WebVRManager.prototype.anyModeToVR_=function(){this.requestFullscreen_(),this.wakelock.request(),this.distorter.patch()},WebVRManager.prototype.vrToMagicWindow_=function(){this.distorter.unpatch(),this.resize_()},WebVRManager.prototype.anyModeToNormal_=function(){this.exitFullscreen_(),this.releasePointerLock_(),this.wakelock.release(),this.distorter.unpatch(),this.resize_()},WebVRManager.prototype.resizeIfNeeded_=function(camera){var size=this.renderer.getSize();size.width==window.innerWidth&&size.height==window.innerHeight||this.resize_()},WebVRManager.prototype.resize_=function(){this.effect.setSize(window.innerWidth,window.innerHeight),this.camera&&(this.camera.aspect=window.innerWidth/window.innerHeight,this.camera.updateProjectionMatrix())},WebVRManager.prototype.onOrientationChange_=function(e){this.updateRotateInstructions_(),this.viewerSelector.hide()},WebVRManager.prototype.updateRotateInstructions_=function(){this.rotateInstructions.disableShowTemporarily(),this.mode==Modes.VR&&!Util.isLandscapeMode()&&Util.isMobile()?this.rotateInstructions.show():this.rotateInstructions.hide()},WebVRManager.prototype.onFullscreenChange_=function(e){null!==document.webkitFullscreenElement&&null!==document.mozFullScreenElement||(this.anyModeToNormal_(),this.setMode_(Modes.NORMAL))},WebVRManager.prototype.requestPointerLock_=function(){var canvas=this.renderer.domElement;canvas.requestPointerLock=canvas.requestPointerLock||canvas.mozRequestPointerLock||canvas.webkitRequestPointerLock,canvas.requestPointerLock&&canvas.requestPointerLock()},WebVRManager.prototype.releasePointerLock_=function(){document.exitPointerLock=document.exitPointerLock||document.mozExitPointerLock||document.webkitExitPointerLock,document.exitPointerLock&&document.exitPointerLock()},WebVRManager.prototype.requestOrientationLock_=function(){screen.orientation&&Util.isMobile()&&screen.orientation.lock("landscape")},WebVRManager.prototype.releaseOrientationLock_=function(){screen.orientation&&screen.orientation.unlock()},WebVRManager.prototype.requestFullscreen_=function(){var canvas=document.body;canvas.requestFullscreen?canvas.requestFullscreen():canvas.mozRequestFullScreen?canvas.mozRequestFullScreen({vrDisplay:this.hmd}):canvas.webkitRequestFullscreen?canvas.webkitRequestFullscreen({vrDisplay:this.hmd}):canvas.msRequestFullscreen&&canvas.msRequestFullscreen({vrDisplay:this.hmd})},WebVRManager.prototype.exitFullscreen_=function(){document.exitFullscreen?document.exitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen?document.webkitExitFullscreen():document.msExitFullscreen&&document.msExitFullscreen()},WebVRManager.prototype.onViewerChanged_=function(viewer){this.deviceInfo.setViewer(viewer),this.distorter.updateDeviceInfo(this.deviceInfo),this.setHMDVRDeviceParams_(viewer),this.emit("viewerchange",viewer)},WebVRManager.prototype.setHMDVRDeviceParams_=function(viewer){this.getDeviceByType_(HMDVRDevice).then(function(hmd){hmd&&(hmd.setFieldOfView&&hmd.setFieldOfView(this.deviceInfo.getFieldOfViewLeftEye(this.isUndistorted),this.deviceInfo.getFieldOfViewRightEye(this.isUndistorted)),hmd.setInterpupillaryDistance&&hmd.setInterpupillaryDistance(viewer.interLensDistance))}.bind(this))},WebVRManager.prototype.onDeviceParamsUpdated_=function(newParams){console.log("DPDB reported that device params were updated."),this.deviceInfo.updateDeviceParams(newParams),this.distorter.updateDeviceInfo(this.deviceInfo)},module.exports=WebVRManager},{"./button-manager.js":2,"./cardboard-distorter.js":3,"./device-info.js":4,"./dpdb.js":8,"./emitter.js":9,"./modes.js":11,"./rotate-instructions.js":12,"./util.js":13,"./viewer-selector.js":14,"./wakelock.js":15}]},{},[10])},{}],7:[function(_dereq_,module,exports){!function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof _dereq_&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof _dereq_&&_dereq_,o=0;oUtil.MAX_TIMESTEP?(console.warn("Invalid timestamps detected. Time step between successive gyroscope sensor samples is very small or not monotonic"),void(this.previousTimestampS=timestampS)):(this.accelerometer.set(-accGravity.x,-accGravity.y,-accGravity.z),
+this.gyroscope.set(rotRate.alpha,rotRate.beta,rotRate.gamma),(this.isIOS||this.isFirefoxAndroid)&&this.gyroscope.multiplyScalar(Math.PI/180),this.filter.addAccelMeasurement(this.accelerometer,timestampS),this.filter.addGyroMeasurement(this.gyroscope,timestampS),void(this.previousTimestampS=timestampS))},FusionPositionSensorVRDevice.prototype.onScreenOrientationChange_=function(screenOrientation){this.setScreenTransform_()},FusionPositionSensorVRDevice.prototype.setScreenTransform_=function(){switch(this.worldToScreenQ.set(0,0,0,1),window.orientation){case 0:break;case 90:this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0,0,1),-Math.PI/2);break;case-90:this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0,0,1),Math.PI/2);break;case 180:}},module.exports=FusionPositionSensorVRDevice},{"./base.js":1,"./complementary-filter.js":3,"./pose-predictor.js":7,"./three-math.js":9,"./touch-panner.js":10,"./util.js":11}],5:[function(_dereq_,module,exports){var WebVRPolyfill=_dereq_("./webvr-polyfill.js");window.WebVRConfig=window.WebVRConfig||{},new WebVRPolyfill},{"./webvr-polyfill.js":12}],6:[function(_dereq_,module,exports){function MouseKeyboardPositionSensorVRDevice(){this.deviceId="webvr-polyfill:mouse-keyboard",this.deviceName="VR Position Device (webvr-polyfill:mouse-keyboard)",window.addEventListener("keydown",this.onKeyDown_.bind(this)),window.addEventListener("mousemove",this.onMouseMove_.bind(this)),window.addEventListener("mousedown",this.onMouseDown_.bind(this)),window.addEventListener("mouseup",this.onMouseUp_.bind(this)),this.phi=0,this.theta=0,this.targetAngle=null,this.euler=new THREE.Euler,this.orientation=new THREE.Quaternion,this.rotateStart=new THREE.Vector2,this.rotateEnd=new THREE.Vector2,this.rotateDelta=new THREE.Vector2}var PositionSensorVRDevice=_dereq_("./base.js").PositionSensorVRDevice,THREE=_dereq_("./three-math.js"),Util=_dereq_("./util.js"),KEY_SPEED=.15,KEY_ANIMATION_DURATION=80,MOUSE_SPEED_X=.5,MOUSE_SPEED_Y=.3;MouseKeyboardPositionSensorVRDevice.prototype=new PositionSensorVRDevice,MouseKeyboardPositionSensorVRDevice.prototype.getState=function(){return this.euler.set(this.phi,this.theta,0,"YXZ"),this.orientation.setFromEuler(this.euler),{hasOrientation:!0,orientation:this.orientation,hasPosition:!1,position:null}},MouseKeyboardPositionSensorVRDevice.prototype.onKeyDown_=function(e){38==e.keyCode?this.animatePhi_(this.phi+KEY_SPEED):39==e.keyCode?this.animateTheta_(this.theta-KEY_SPEED):40==e.keyCode?this.animatePhi_(this.phi-KEY_SPEED):37==e.keyCode&&this.animateTheta_(this.theta+KEY_SPEED)},MouseKeyboardPositionSensorVRDevice.prototype.animateTheta_=function(targetAngle){this.animateKeyTransitions_("theta",targetAngle)},MouseKeyboardPositionSensorVRDevice.prototype.animatePhi_=function(targetAngle){targetAngle=Util.clamp(targetAngle,-Math.PI/2,Math.PI/2),this.animateKeyTransitions_("phi",targetAngle)},MouseKeyboardPositionSensorVRDevice.prototype.animateKeyTransitions_=function(angleName,targetAngle){this.angleAnimation&&clearInterval(this.angleAnimation);var startAngle=this[angleName],startTime=new Date;this.angleAnimation=setInterval(function(){var elapsed=new Date-startTime;if(elapsed>=KEY_ANIMATION_DURATION)return this[angleName]=targetAngle,void clearInterval(this.angleAnimation);var percent=elapsed/KEY_ANIMATION_DURATION;this[angleName]=startAngle+(targetAngle-startAngle)*percent}.bind(this),1e3/60)},MouseKeyboardPositionSensorVRDevice.prototype.onMouseDown_=function(e){this.rotateStart.set(e.clientX,e.clientY),this.isDragging=!0},MouseKeyboardPositionSensorVRDevice.prototype.onMouseMove_=function(e){if(this.isDragging||this.isPointerLocked_()){if(this.isPointerLocked_()){var movementX=e.movementX||e.mozMovementX||0,movementY=e.movementY||e.mozMovementY||0;this.rotateEnd.set(this.rotateStart.x-movementX,this.rotateStart.y-movementY)}else this.rotateEnd.set(e.clientX,e.clientY);this.rotateDelta.subVectors(this.rotateEnd,this.rotateStart),this.rotateStart.copy(this.rotateEnd);var element=document.body;this.phi+=2*Math.PI*this.rotateDelta.y/element.clientHeight*MOUSE_SPEED_Y,this.theta+=2*Math.PI*this.rotateDelta.x/element.clientWidth*MOUSE_SPEED_X,this.phi=Util.clamp(this.phi,-Math.PI/2,Math.PI/2)}},MouseKeyboardPositionSensorVRDevice.prototype.onMouseUp_=function(e){this.isDragging=!1},MouseKeyboardPositionSensorVRDevice.prototype.isPointerLocked_=function(){var el=document.pointerLockElement||document.mozPointerLockElement||document.webkitPointerLockElement;return void 0!==el},MouseKeyboardPositionSensorVRDevice.prototype.resetSensor=function(){console.error("Not implemented yet.")},module.exports=MouseKeyboardPositionSensorVRDevice},{"./base.js":1,"./three-math.js":9,"./util.js":11}],7:[function(_dereq_,module,exports){function PosePredictor(predictionTimeS){this.predictionTimeS=predictionTimeS,this.previousQ=new THREE.Quaternion,this.previousTimestampS=null,this.deltaQ=new THREE.Quaternion,this.outQ=new THREE.Quaternion}var THREE=_dereq_("./three-math.js"),DEBUG=!1;PosePredictor.prototype.getPrediction=function(currentQ,gyro,timestampS){if(!this.previousTimestampS)return this.previousQ.copy(currentQ),this.previousTimestampS=timestampS,currentQ;var axis=new THREE.Vector3;axis.copy(gyro),axis.normalize();var angularSpeed=gyro.length();if(angularSpeed0?(s=.5/Math.sqrt(trace+1),this._w=.25/s,this._x=(m32-m23)*s,this._y=(m13-m31)*s,this._z=(m21-m12)*s):m11>m22&&m11>m33?(s=2*Math.sqrt(1+m11-m22-m33),this._w=(m32-m23)/s,this._x=.25*s,this._y=(m12+m21)/s,this._z=(m13+m31)/s):m22>m33?(s=2*Math.sqrt(1+m22-m11-m33),this._w=(m13-m31)/s,this._x=(m12+m21)/s,this._y=.25*s,this._z=(m23+m32)/s):(s=2*Math.sqrt(1+m33-m11-m22),this._w=(m21-m12)/s,this._x=(m13+m31)/s,this._y=(m23+m32)/s,this._z=.25*s),this.onChangeCallback(),this},setFromUnitVectors:function(){var v1,r,EPS=1e-6;return function(vFrom,vTo){return void 0===v1&&(v1=new THREE.Vector3),r=vFrom.dot(vTo)+1,EPS>r?(r=0,Math.abs(vFrom.x)>Math.abs(vFrom.z)?v1.set(-vFrom.y,vFrom.x,0):v1.set(0,-vFrom.z,vFrom.y)):v1.crossVectors(vFrom,vTo),this._x=v1.x,this._y=v1.y,this._z=v1.z,this._w=r,this.normalize(),this}}(),inverse:function(){return this.conjugate().normalize(),this},conjugate:function(){return this._x*=-1,this._y*=-1,this._z*=-1,this.onChangeCallback(),this},dot:function(v){return this._x*v._x+this._y*v._y+this._z*v._z+this._w*v._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var l=this.length();return 0===l?(this._x=0,this._y=0,this._z=0,this._w=1):(l=1/l,this._x=this._x*l,this._y=this._y*l,this._z=this._z*l,this._w=this._w*l),this.onChangeCallback(),this},multiply:function(q,p){return void 0!==p?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(q,p)):this.multiplyQuaternions(this,q)},multiplyQuaternions:function(a,b){var qax=a._x,qay=a._y,qaz=a._z,qaw=a._w,qbx=b._x,qby=b._y,qbz=b._z,qbw=b._w;return this._x=qax*qbw+qaw*qbx+qay*qbz-qaz*qby,this._y=qay*qbw+qaw*qby+qaz*qbx-qax*qbz,this._z=qaz*qbw+qaw*qbz+qax*qby-qay*qbx,this._w=qaw*qbw-qax*qbx-qay*qby-qaz*qbz,this.onChangeCallback(),this},multiplyVector3:function(vector){return console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."),vector.applyQuaternion(this)},slerp:function(qb,t){if(0===t)return this;if(1===t)return this.copy(qb);var x=this._x,y=this._y,z=this._z,w=this._w,cosHalfTheta=w*qb._w+x*qb._x+y*qb._y+z*qb._z;if(0>cosHalfTheta?(this._w=-qb._w,this._x=-qb._x,this._y=-qb._y,this._z=-qb._z,cosHalfTheta=-cosHalfTheta):this.copy(qb),cosHalfTheta>=1)return this._w=w,this._x=x,this._y=y,this._z=z,this;var halfTheta=Math.acos(cosHalfTheta),sinHalfTheta=Math.sqrt(1-cosHalfTheta*cosHalfTheta);if(Math.abs(sinHalfTheta)<.001)return this._w=.5*(w+this._w),this._x=.5*(x+this._x),this._y=.5*(y+this._y),this._z=.5*(z+this._z),this;var ratioA=Math.sin((1-t)*halfTheta)/sinHalfTheta,ratioB=Math.sin(t*halfTheta)/sinHalfTheta;return this._w=w*ratioA+this._w*ratioB,this._x=x*ratioA+this._x*ratioB,this._y=y*ratioA+this._y*ratioB,this._z=z*ratioA+this._z*ratioB,this.onChangeCallback(),this},equals:function(quaternion){return quaternion._x===this._x&&quaternion._y===this._y&&quaternion._z===this._z&&quaternion._w===this._w},fromArray:function(array,offset){return void 0===offset&&(offset=0),this._x=array[offset],this._y=array[offset+1],this._z=array[offset+2],this._w=array[offset+3],this.onChangeCallback(),this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this._x,array[offset+1]=this._y,array[offset+2]=this._z,array[offset+3]=this._w,array},onChange:function(callback){return this.onChangeCallback=callback,this},onChangeCallback:function(){},clone:function(){return new THREE.Quaternion(this._x,this._y,this._z,this._w)}},THREE.Quaternion.slerp=function(qa,qb,qm,t){return qm.copy(qa).slerp(qb,t)},THREE.Vector2=function(x,y){this.x=x||0,this.y=y||0},THREE.Vector2.prototype={constructor:THREE.Vector2,set:function(x,y){return this.x=x,this.y=y,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+index)}},copy:function(v){return this.x=v.x,this.y=v.y,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this)},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this},addScalar:function(s){return this.x+=s,this.y+=s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this)},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this},multiply:function(v){return this.x*=v.x,this.y*=v.y,this},multiplyScalar:function(s){return this.x*=s,this.y*=s,this},divide:function(v){return this.x/=v.x,this.y/=v.y,this},divideScalar:function(scalar){if(0!==scalar){var invScalar=1/scalar;this.x*=invScalar,this.y*=invScalar}else this.x=0,this.y=0;return this},min:function(v){return this.x>v.x&&(this.x=v.x),this.y>v.y&&(this.y=v.y),this},max:function(v){return this.xmax.x&&(this.x=max.x),this.ymax.y&&(this.y=max.y),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector2,max=new THREE.Vector2),min.set(minVal,minVal),max.set(maxVal,maxVal),this.clamp(min,max)}}(),floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this},negate:function(){return this.x=-this.x,this.y=-this.y,this},dot:function(v){return this.x*v.x+this.y*v.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},normalize:function(){return this.divideScalar(this.length())},distanceTo:function(v){return Math.sqrt(this.distanceToSquared(v))},distanceToSquared:function(v){var dx=this.x-v.x,dy=this.y-v.y;return dx*dx+dy*dy},setLength:function(l){var oldLength=this.length();return 0!==oldLength&&l!==oldLength&&this.multiplyScalar(l/oldLength),this},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this},equals:function(v){return v.x===this.x&&v.y===this.y},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this},clone:function(){return new THREE.Vector2(this.x,this.y)}},THREE.Vector3=function(x,y,z){this.x=x||0,this.y=y||0,this.z=z||0},THREE.Vector3.prototype={constructor:THREE.Vector3,set:function(x,y,z){return this.x=x,this.y=y,this.z=z,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setZ:function(z){return this.z=z,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;case 2:this.z=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+index)}},copy:function(v){return this.x=v.x,this.y=v.y,this.z=v.z,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this.z+=v.z,this)},addScalar:function(s){return this.x+=s,this.y+=s,this.z+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this.z=a.z+b.z,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this.z-=v.z,this)},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this.z=a.z-b.z,this},multiply:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(v,w)):(this.x*=v.x,this.y*=v.y,this.z*=v.z,this)},multiplyScalar:function(scalar){return this.x*=scalar,this.y*=scalar,this.z*=scalar,this},multiplyVectors:function(a,b){return this.x=a.x*b.x,this.y=a.y*b.y,this.z=a.z*b.z,this},applyEuler:function(){var quaternion;return function(euler){return euler instanceof THREE.Euler==!1&&console.error("THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order."),void 0===quaternion&&(quaternion=new THREE.Quaternion),this.applyQuaternion(quaternion.setFromEuler(euler)),this}}(),applyAxisAngle:function(){var quaternion;return function(axis,angle){return void 0===quaternion&&(quaternion=new THREE.Quaternion),this.applyQuaternion(quaternion.setFromAxisAngle(axis,angle)),this}}(),applyMatrix3:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[3]*y+e[6]*z,this.y=e[1]*x+e[4]*y+e[7]*z,this.z=e[2]*x+e[5]*y+e[8]*z,this},applyMatrix4:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z+e[12],this.y=e[1]*x+e[5]*y+e[9]*z+e[13],this.z=e[2]*x+e[6]*y+e[10]*z+e[14],this},applyProjection:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements,d=1/(e[3]*x+e[7]*y+e[11]*z+e[15]);return this.x=(e[0]*x+e[4]*y+e[8]*z+e[12])*d,this.y=(e[1]*x+e[5]*y+e[9]*z+e[13])*d,this.z=(e[2]*x+e[6]*y+e[10]*z+e[14])*d,this},applyQuaternion:function(q){var x=this.x,y=this.y,z=this.z,qx=q.x,qy=q.y,qz=q.z,qw=q.w,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;return this.x=ix*qw+iw*-qx+iy*-qz-iz*-qy,this.y=iy*qw+iw*-qy+iz*-qx-ix*-qz,this.z=iz*qw+iw*-qz+ix*-qy-iy*-qx,this},project:function(){var matrix;return function(camera){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.multiplyMatrices(camera.projectionMatrix,matrix.getInverse(camera.matrixWorld)),this.applyProjection(matrix)}}(),unproject:function(){var matrix;return function(camera){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.multiplyMatrices(camera.matrixWorld,matrix.getInverse(camera.projectionMatrix)),this.applyProjection(matrix)}}(),transformDirection:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z,this.y=e[1]*x+e[5]*y+e[9]*z,this.z=e[2]*x+e[6]*y+e[10]*z,this.normalize(),this},divide:function(v){return this.x/=v.x,this.y/=v.y,this.z/=v.z,this},divideScalar:function(scalar){if(0!==scalar){var invScalar=1/scalar;this.x*=invScalar,this.y*=invScalar,this.z*=invScalar}else this.x=0,this.y=0,this.z=0;return this},min:function(v){return this.x>v.x&&(this.x=v.x),this.y>v.y&&(this.y=v.y),this.z>v.z&&(this.z=v.z),this},max:function(v){return this.xmax.x&&(this.x=max.x),this.ymax.y&&(this.y=max.y),this.zmax.z&&(this.z=max.z),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector3,max=new THREE.Vector3),min.set(minVal,minVal,minVal),max.set(maxVal,maxVal,maxVal),this.clamp(min,max)}}(),floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this},dot:function(v){return this.x*v.x+this.y*v.y+this.z*v.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)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length())},setLength:function(l){var oldLength=this.length();return 0!==oldLength&&l!==oldLength&&this.multiplyScalar(l/oldLength),this},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this.z+=(v.z-this.z)*alpha,this},cross:function(v,w){if(void 0!==w)return console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(v,w);var x=this.x,y=this.y,z=this.z;return this.x=y*v.z-z*v.y,this.y=z*v.x-x*v.z,this.z=x*v.y-y*v.x,this},crossVectors:function(a,b){var ax=a.x,ay=a.y,az=a.z,bx=b.x,by=b.y,bz=b.z;return this.x=ay*bz-az*by,this.y=az*bx-ax*bz,this.z=ax*by-ay*bx,this},projectOnVector:function(){var v1,dot;return function(vector){return void 0===v1&&(v1=new THREE.Vector3),v1.copy(vector).normalize(),dot=this.dot(v1),this.copy(v1).multiplyScalar(dot)}}(),projectOnPlane:function(){var v1;return function(planeNormal){return void 0===v1&&(v1=new THREE.Vector3),v1.copy(this).projectOnVector(planeNormal),this.sub(v1)}}(),reflect:function(){var v1;return function(normal){return void 0===v1&&(v1=new THREE.Vector3),this.sub(v1.copy(normal).multiplyScalar(2*this.dot(normal)))}}(),angleTo:function(v){var theta=this.dot(v)/(this.length()*v.length());return Math.acos(THREE.Math.clamp(theta,-1,1))},distanceTo:function(v){return Math.sqrt(this.distanceToSquared(v))},distanceToSquared:function(v){var dx=this.x-v.x,dy=this.y-v.y,dz=this.z-v.z;return dx*dx+dy*dy+dz*dz},setEulerFromRotationMatrix:function(m,order){console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.")},setEulerFromQuaternion:function(q,order){console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.")},getPositionFromMatrix:function(m){return console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."),this.setFromMatrixPosition(m)},getScaleFromMatrix:function(m){return console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."),this.setFromMatrixScale(m)},getColumnFromMatrix:function(index,matrix){return console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."),this.setFromMatrixColumn(index,matrix)},setFromMatrixPosition:function(m){return this.x=m.elements[12],this.y=m.elements[13],this.z=m.elements[14],this},setFromMatrixScale:function(m){var sx=this.set(m.elements[0],m.elements[1],m.elements[2]).length(),sy=this.set(m.elements[4],m.elements[5],m.elements[6]).length(),sz=this.set(m.elements[8],m.elements[9],m.elements[10]).length();return this.x=sx,this.y=sy,this.z=sz,this},setFromMatrixColumn:function(index,matrix){var offset=4*index,me=matrix.elements;return this.x=me[offset],this.y=me[offset+1],this.z=me[offset+2],this},equals:function(v){return v.x===this.x&&v.y===this.y&&v.z===this.z},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this.z=array[offset+2],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array[offset+2]=this.z,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this.z=attribute.array[index+2],this},clone:function(){return new THREE.Vector3(this.x,this.y,this.z)}},THREE.Euler=function(x,y,z,order){this._x=x||0,this._y=y||0,this._z=z||0,this._order=order||THREE.Euler.DefaultOrder},THREE.Euler.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"],THREE.Euler.DefaultOrder="XYZ",THREE.Euler.prototype={constructor:THREE.Euler,_x:0,_y:0,_z:0,_order:THREE.Euler.DefaultOrder,get x(){return this._x},set x(value){this._x=value,this.onChangeCallback()},get y(){return this._y},set y(value){this._y=value,this.onChangeCallback()},get z(){return this._z},set z(value){this._z=value,this.onChangeCallback()},get order(){return this._order},set order(value){this._order=value,this.onChangeCallback()},set:function(x,y,z,order){return this._x=x,this._y=y,this._z=z,this._order=order||this._order,this.onChangeCallback(),this},copy:function(euler){return this._x=euler._x,this._y=euler._y,this._z=euler._z,this._order=euler._order,this.onChangeCallback(),this},setFromRotationMatrix:function(m,order,update){var clamp=THREE.Math.clamp,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10];return order=order||this._order,"XYZ"===order?(this._y=Math.asin(clamp(m13,-1,1)),Math.abs(m13)<.99999?(this._x=Math.atan2(-m23,m33),this._z=Math.atan2(-m12,m11)):(this._x=Math.atan2(m32,m22),this._z=0)):"YXZ"===order?(this._x=Math.asin(-clamp(m23,-1,1)),Math.abs(m23)<.99999?(this._y=Math.atan2(m13,m33),this._z=Math.atan2(m21,m22)):(this._y=Math.atan2(-m31,m11),this._z=0)):"ZXY"===order?(this._x=Math.asin(clamp(m32,-1,1)),Math.abs(m32)<.99999?(this._y=Math.atan2(-m31,m33),this._z=Math.atan2(-m12,m22)):(this._y=0,this._z=Math.atan2(m21,m11))):"ZYX"===order?(this._y=Math.asin(-clamp(m31,-1,1)),Math.abs(m31)<.99999?(this._x=Math.atan2(m32,m33),this._z=Math.atan2(m21,m11)):(this._x=0,this._z=Math.atan2(-m12,m22))):"YZX"===order?(this._z=Math.asin(clamp(m21,-1,1)),Math.abs(m21)<.99999?(this._x=Math.atan2(-m23,m22),this._y=Math.atan2(-m31,m11)):(this._x=0,this._y=Math.atan2(m13,m33))):"XZY"===order?(this._z=Math.asin(-clamp(m12,-1,1)),Math.abs(m12)<.99999?(this._x=Math.atan2(m32,m22),this._y=Math.atan2(m13,m11)):(this._x=Math.atan2(-m23,m33),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+order),this._order=order,update!==!1&&this.onChangeCallback(),this},setFromQuaternion:function(){var matrix;return function(q,order,update){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.makeRotationFromQuaternion(q),this.setFromRotationMatrix(matrix,order,update),this}}(),setFromVector3:function(v,order){return this.set(v.x,v.y,v.z,order||this._order)},reorder:function(){var q=new THREE.Quaternion;return function(newOrder){q.setFromEuler(this),this.setFromQuaternion(q,newOrder)}}(),equals:function(euler){return euler._x===this._x&&euler._y===this._y&&euler._z===this._z&&euler._order===this._order},fromArray:function(array){return this._x=array[0],this._y=array[1],this._z=array[2],void 0!==array[3]&&(this._order=array[3]),this.onChangeCallback(),this},toArray:function(){return[this._x,this._y,this._z,this._order]},toVector3:function(optionalResult){return optionalResult?optionalResult.set(this._x,this._y,this._z):new THREE.Vector3(this._x,this._y,this._z)},onChange:function(callback){return this.onChangeCallback=callback,this},onChangeCallback:function(){},clone:function(){return new THREE.Euler(this._x,this._y,this._z,this._order)}},THREE.Math={generateUUID:function(){var r,chars="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),uuid=new Array(36),rnd=0;return function(){for(var i=0;36>i;i++)8==i||13==i||18==i||23==i?uuid[i]="-":14==i?uuid[i]="4":(2>=rnd&&(rnd=33554432+16777216*Math.random()|0),r=15&rnd,rnd>>=4,uuid[i]=chars[19==i?3&r|8:r]);return uuid.join("")}}(),clamp:function(x,a,b){return a>x?a:x>b?b:x},clampBottom:function(x,a){return a>x?a:x},mapLinear:function(x,a1,a2,b1,b2){return b1+(x-a1)*(b2-b1)/(a2-a1)},smoothstep:function(x,min,max){return min>=x?0:x>=max?1:(x=(x-min)/(max-min),x*x*(3-2*x))},smootherstep:function(x,min,max){return min>=x?0:x>=max?1:(x=(x-min)/(max-min),x*x*x*(x*(6*x-15)+10))},random16:function(){return(65280*Math.random()+255*Math.random())/65535},randInt:function(low,high){return Math.floor(this.randFloat(low,high))},randFloat:function(low,high){return low+Math.random()*(high-low)},randFloatSpread:function(range){return range*(.5-Math.random())},degToRad:function(){var degreeToRadiansFactor=Math.PI/180;return function(degrees){return degrees*degreeToRadiansFactor}}(),radToDeg:function(){var radianToDegreesFactor=180/Math.PI;return function(radians){return radians*radianToDegreesFactor}}(),isPowerOfTwo:function(value){return 0===(value&value-1)&&0!==value},nextPowerOfTwo:function(value){return value--,value|=value>>1,value|=value>>2,value|=value>>4,value|=value>>8,value|=value>>16,value++,value}}),module.exports=THREE},{}],10:[function(_dereq_,module,exports){function TouchPanner(){window.addEventListener("touchstart",this.onTouchStart_.bind(this)),window.addEventListener("touchmove",this.onTouchMove_.bind(this)),window.addEventListener("touchend",this.onTouchEnd_.bind(this)),this.isTouching=!1,this.rotateStart=new THREE.Vector2,this.rotateEnd=new THREE.Vector2,this.rotateDelta=new THREE.Vector2,this.theta=0,this.orientation=new THREE.Quaternion}var THREE=_dereq_("./three-math.js"),Util=_dereq_("./util.js"),ROTATE_SPEED=.5;TouchPanner.prototype.getOrientation=function(){return this.orientation.setFromEuler(new THREE.Euler(0,0,this.theta)),this.orientation},TouchPanner.prototype.resetSensor=function(){this.theta=0},TouchPanner.prototype.onTouchStart_=function(e){1==e.touches.length&&(this.rotateStart.set(e.touches[0].pageX,e.touches[0].pageY),this.isTouching=!0)},TouchPanner.prototype.onTouchMove_=function(e){if(this.isTouching){this.rotateEnd.set(e.touches[0].pageX,e.touches[0].pageY),this.rotateDelta.subVectors(this.rotateEnd,this.rotateStart),this.rotateStart.copy(this.rotateEnd),Util.isIOS()&&(this.rotateDelta.x*=-1);var element=document.body;this.theta+=2*Math.PI*this.rotateDelta.x/element.clientWidth*ROTATE_SPEED}},TouchPanner.prototype.onTouchEnd_=function(e){this.isTouching=!1},module.exports=TouchPanner},{"./three-math.js":9,"./util.js":11}],11:[function(_dereq_,module,exports){var Util=window.Util||{};Util.MIN_TIMESTEP=.001,Util.MAX_TIMESTEP=1,Util.clamp=function(value,min,max){return Math.min(Math.max(min,value),max)},Util.isIOS=function(){return/iPad|iPhone|iPod/.test(navigator.platform)},Util.isFirefoxAndroid=function(){return-1!==navigator.userAgent.indexOf("Firefox")&&-1!==navigator.userAgent.indexOf("Android")},Util.isTimestampDeltaValid=function(timestampDeltaS){return isNaN(timestampDeltaS)?!1:timestampDeltaS<=Util.MIN_TIMESTEP?!1:!(timestampDeltaS>Util.MAX_TIMESTEP)},module.exports=Util},{}],12:[function(_dereq_,module,exports){function WebVRPolyfill(){this.devices=[],this.isWebVRAvailable()||this.enablePolyfill()}var CardboardHMDVRDevice=_dereq_("./cardboard-hmd-vr-device.js"),FusionPositionSensorVRDevice=_dereq_("./fusion-position-sensor-vr-device.js"),MouseKeyboardPositionSensorVRDevice=_dereq_("./mouse-keyboard-position-sensor-vr-device.js"),HMDVRDevice=_dereq_("./base.js").HMDVRDevice,PositionSensorVRDevice=_dereq_("./base.js").PositionSensorVRDevice;
+WebVRPolyfill.prototype.isWebVRAvailable=function(){return"getVRDevices"in navigator||"mozGetVRDevices"in navigator},WebVRPolyfill.prototype.enablePolyfill=function(){this.isCardboardCompatible()&&this.devices.push(new CardboardHMDVRDevice),this.isMobile()?this.devices.push(new FusionPositionSensorVRDevice):WebVRConfig.MOUSE_KEYBOARD_CONTROLS_DISABLED||this.devices.push(new MouseKeyboardPositionSensorVRDevice),navigator.getVRDevices=this.getVRDevices.bind(this),window.HMDVRDevice=HMDVRDevice,window.PositionSensorVRDevice=PositionSensorVRDevice},WebVRPolyfill.prototype.getVRDevices=function(){var devices=this.devices;return new Promise(function(resolve,reject){try{resolve(devices)}catch(e){reject(e)}})},WebVRPolyfill.prototype.isMobile=function(){return/Android/i.test(navigator.userAgent)||/iPhone|iPad|iPod/i.test(navigator.userAgent)},WebVRPolyfill.prototype.isCardboardCompatible=function(){return this.isMobile()||WebVRConfig.FORCE_ENABLE_VR},module.exports=WebVRPolyfill},{"./base.js":1,"./cardboard-hmd-vr-device.js":2,"./fusion-position-sensor-vr-device.js":4,"./mouse-keyboard-position-sensor-vr-device.js":6}]},{},[5])},{}],8:[function(_dereq_,module,exports){function Analytics(){!function(i,s,o,g,r,a,m){i.GoogleAnalyticsObject=r,i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date,a=s.createElement(o),m=s.getElementsByTagName(o)[0],a.async=1,a.src=g,m.parentNode.insertBefore(a,m)}(window,document,"script","//www.google-analytics.com/analytics.js","ga"),ga("create","UA-35315454-8","auto"),ga("send","pageview"),this.lastModeChangeTime=window.performance.now(),this.lastModeLabel=WebVRManager.Modes.UNKNOWN}var MODE_LABELS={0:"UNKNOWN",1:"NORMAL",2:"MAGIC_WINDOW",3:"VR"};Analytics.prototype.logModeChanged=function(mode){var modeLabel=MODE_LABELS[mode],lastModeLabel=MODE_LABELS[this.lastMode];console.log("Analytics: going from mode %s to %s",lastModeLabel,modeLabel),ga("send","screenview",{appName:"EmbedVR",screenName:modeLabel});var now=window.performance.now(),msSinceLastModeChange=Math.round(now-this.lastModeChangeTime);ga("send","timing","Time spent in mode",lastModeLabel,msSinceLastModeChange),this.lastModeChangeTime=now,this.lastMode=mode},window.analytics=new Analytics},{}],9:[function(_dereq_,module,exports){function DeviceMotionReceiver(){window.addEventListener("message",this.onMessage_.bind(this),!1)}DeviceMotionReceiver.prototype.onMessage_=function(event){var message=event.data;return"DeviceMotion"!==message.type?void console.warn("Got unknown message of type %s from %s",message.type,message.origin):(console.log("onMessage_",event),void this.synthesizeDeviceMotionEvent_(message.deviceMotionEvent))},DeviceMotionReceiver.prototype.synthesizeDeviceMotionEvent_=function(eventData){var type="devicemotion-iframe",canBubble=!1,cancelable=!1,dme=document.createEvent("DeviceMotionEvent");dme.initDeviceMotionEvent(type,canBubble,cancelable,eventData.acceleration,eventData.accelerationIncludingGravity,eventData.rotationRate,eventData.interval),window.dispatchEvent(dme)},module.exports=DeviceMotionReceiver},{}],10:[function(_dereq_,module,exports){function Emitter(){this.initEmitter()}Emitter.prototype.initEmitter=function(){this.callbacks={}},Emitter.prototype.emit=function(eventName){var callbacks=this.callbacks[eventName];if(!callbacks)return void console.log("No valid callback specified.");var args=[].slice.call(arguments);args.shift();for(var i=0;ij;j++)uvs[i][j].x*=p.scaleX,uvs[i][j].x+=p.offsetX,uvs[i][j].y*=p.scaleY,uvs[i][j].y+=p.offsetY;var material=new THREE.MeshBasicMaterial({map:texture});this.distorter.setMap(texture);var out=new THREE.Mesh(geometry,material);return out.renderOrder=-1,out},PhotosphereRenderer.prototype.createScene_=function(opt_params){var scene=new THREE.Scene;scene.add(new THREE.PointLight(16777215));var photoGroup=new THREE.Object3D;return photoGroup.name="photo",scene.add(photoGroup),scene},PhotosphereRenderer.prototype.updateMaterial_=function(material_FOO){for(var i=0;i1)for(var i=1;it;t+=2){var e=X[t],n=X[t+1];e(n),X[t]=void 0,X[t+1]=void 0}G=0}function f(){try{var t=_dereq_,e=t("vertx");return U=e.runOnLoop||e.runOnContext,i()}catch(n){return c()}}function l(t,e){var n=this,r=n._state;if(r===et&&!t||r===nt&&!e)return this;var o=new this.constructor(p),i=n._result;if(r){var s=arguments[r-1];H(function(){C(r,o,s,i)})}else j(n,o,t,e);return o}function h(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(p);return g(n,t),n}function p(){}function _(){return new TypeError("You cannot resolve a promise with itself")}function v(){return new TypeError("A promises callback cannot return that same promise.")}function d(t){try{return t.then}catch(e){return rt.error=e,rt}}function y(t,e,n,r){try{t.call(e,n,r)}catch(o){return o}}function m(t,e,n){H(function(t){var r=!1,o=y(n,e,function(n){r||(r=!0,e!==n?g(t,n):E(t,n))},function(e){r||(r=!0,S(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&o&&(r=!0,S(t,o))},t)}function w(t,e){e._state===et?E(t,e._result):e._state===nt?S(t,e._result):j(e,void 0,function(e){g(t,e)},function(e){S(t,e)})}function b(t,n,r){n.constructor===t.constructor&&r===Z&&constructor.resolve===$?w(t,n):r===rt?S(t,rt.error):void 0===r?E(t,n):e(r)?m(t,n,r):E(t,n)}function g(e,n){e===n?S(e,_()):t(n)?b(e,n,d(n)):E(e,n)}function A(t){t._onerror&&t._onerror(t._result),T(t)}function E(t,e){t._state===tt&&(t._result=e,t._state=et,0!==t._subscribers.length&&H(T,t))}function S(t,e){t._state===tt&&(t._state=nt,t._result=e,H(A,t))}function j(t,e,n,r){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+et]=n,o[i+nt]=r,0===i&&t._state&&H(T,t)}function T(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,o,i=t._result,s=0;ss;s++)j(r.resolve(t[s]),void 0,e,n);return o}function Y(t){var e=this,n=new e(p);return S(n,t),n}function q(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function F(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function D(t){this._id=ct++,this._state=void 0,this._result=void 0,this._subscribers=[],p!==t&&("function"!=typeof t&&q(),this instanceof D?M(this,t):F())}function K(t,e){this._instanceConstructor=t,this.promise=new t(p),Array.isArray(e)?(this._input=e,this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?E(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&E(this.promise,this._result))):S(this.promise,this._validationError())}function L(){var t;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;(!n||"[object Promise]"!==Object.prototype.toString.call(n.resolve())||n.cast)&&(t.Promise=at)}var N;N=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var U,W,z,B=N,G=0,H=function(t,e){X[G]=t,X[G+1]=e,G+=2,2===G&&(W?W(a):z())},I="undefined"!=typeof window?window:void 0,J=I||{},Q=J.MutationObserver||J.WebKitMutationObserver,R="undefined"!=typeof process&&"[object process]"==={}.toString.call(process),V="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,X=new Array(1e3);z=R?o():Q?s():V?u():void 0===I&&"function"==typeof _dereq_?f():c();var Z=l,$=h,tt=void 0,et=1,nt=2,rt=new P,ot=new P,it=O,st=k,ut=Y,ct=0,at=D;D.all=it,D.race=st,D.resolve=$,D.reject=ut,D._setScheduler=n,D._setAsap=r,D._asap=H,D.prototype={constructor:D,then:Z,"catch":function(t){return this.then(null,t)}};var ft=K;K.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},K.prototype._enumerate=function(){for(var t=this.length,e=this._input,n=0;this._state===tt&&t>n;n++)this._eachEntry(e[n],n)},K.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===$){var o=d(t);if(o===Z&&t._state!==tt)this._settledAt(t._state,e,t._result);else if("function"!=typeof o)this._remaining--,this._result[e]=t;else if(n===at){var i=new n(p);b(i,t,o),this._willSettleAt(i,e)}else this._willSettleAt(new n(function(e){e(t)}),e)}else this._willSettleAt(r(t),e)},K.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===tt&&(this._remaining--,t===nt?S(r,n):this._result[e]=n),0===this._remaining&&E(r,this._result)},K.prototype._willSettleAt=function(t,e){var n=this;j(t,void 0,function(t){n._settledAt(et,e,t)},function(t){n._settledAt(nt,e,t)})};var lt=L,ht={Promise:at,polyfill:lt};"function"==typeof define&&define.amd?define(function(){return ht}):"undefined"!=typeof module&&module.exports?module.exports=ht:"undefined"!=typeof this&&(this.ES6Promise=ht),lt()}).call(this);
+}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"_process":18}],2:[function(_dereq_,module,exports){
+// stats.js - http://github.com/mrdoob/stats.js
+var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";
+i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div");
+k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display=
+"block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:12,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height=
+a+"px",m=b,r=0);return b},update:function(){l=this.end()}}};"object"===typeof module&&(module.exports=Stats);
+
+},{}],3:[function(_dereq_,module,exports){
+/**
+ * @author dmarcos / https://github.com/dmarcos
+ * @author mrdoob / http://mrdoob.com
+ */
+
+var VRControls = function ( object, onError ) {
+
+ var scope = this;
+
+ var vrInputs = [];
+
+ function filterInvalidDevices( devices ) {
+
+ // Exclude Cardboard position sensor if Oculus exists.
+
+ var oculusDevices = devices.filter( function ( device ) {
+
+ return device.deviceName.toLowerCase().indexOf( 'oculus' ) !== - 1;
+
+ } );
+
+ if ( oculusDevices.length >= 1 ) {
+
+ return devices.filter( function ( device ) {
+
+ return device.deviceName.toLowerCase().indexOf( 'cardboard' ) === - 1;
+
+ } );
+
+ } else {
+
+ return devices;
+
+ }
+
+ }
+
+ function gotVRDevices( devices ) {
+
+ devices = filterInvalidDevices( devices );
+
+ for ( var i = 0; i < devices.length; i ++ ) {
+
+ if ( devices[ i ] instanceof PositionSensorVRDevice ) {
+
+ vrInputs.push( devices[ i ] );
+
+ }
+
+ }
+
+ if ( onError ) onError( 'HMD not available' );
+
+ }
+
+ if ( navigator.getVRDevices ) {
+
+ navigator.getVRDevices().then( gotVRDevices );
+
+ }
+
+ // the Rift SDK returns the position in meters
+ // this scale factor allows the user to define how meters
+ // are converted to scene units.
+
+ this.scale = 1;
+
+ this.update = function () {
+
+ for ( var i = 0; i < vrInputs.length; i ++ ) {
+
+ var vrInput = vrInputs[ i ];
+
+ var state = vrInput.getState();
+
+ if ( state.orientation !== null ) {
+
+ object.quaternion.copy( state.orientation );
+
+ }
+
+ if ( state.position !== null ) {
+
+ object.position.copy( state.position ).multiplyScalar( scope.scale );
+
+ }
+
+ }
+
+ };
+
+ this.resetSensor = function () {
+
+ for ( var i = 0; i < vrInputs.length; i ++ ) {
+
+ var vrInput = vrInputs[ i ];
+
+ if ( vrInput.resetSensor !== undefined ) {
+
+ vrInput.resetSensor();
+
+ } else if ( vrInput.zeroSensor !== undefined ) {
+
+ vrInput.zeroSensor();
+
+ }
+
+ }
+
+ };
+
+ this.zeroSensor = function () {
+
+ console.warn( 'THREE.VRControls: .zeroSensor() is now .resetSensor().' );
+ this.resetSensor();
+
+ };
+
+ this.dispose = function () {
+
+ vrInputs = [];
+
+ };
+
+};
+
+try {
+ module.exports = VRControls;
+} catch (e) {
+ THREE.VRControls = VRControls;
+}
+
+},{}],4:[function(_dereq_,module,exports){
+/**
+ * @author dmarcos / https://github.com/dmarcos
+ * @author mrdoob / http://mrdoob.com
+ *
+ * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html
+ *
+ * Firefox: http://mozvr.com/downloads/
+ * Chromium: https://drive.google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list
+ *
+ */
+
+var VREffect = function ( renderer, onError ) {
+
+ var vrHMD;
+ var eyeTranslationL, eyeFOVL, rectL;
+ var eyeTranslationR, eyeFOVR, rectR;
+
+ function gotVRDevices( devices ) {
+
+ for ( var i = 0; i < devices.length; i ++ ) {
+
+ if ( devices[ i ] instanceof HMDVRDevice ) {
+
+ vrHMD = devices[ i ];
+
+ break; // We keep the first we encounter
+
+ }
+
+ }
+
+ if ( vrHMD === undefined ) {
+
+ if ( onError ) onError( 'HMD not available' );
+
+ }
+
+ }
+
+ if ( navigator.getVRDevices ) {
+
+ navigator.getVRDevices().then( gotVRDevices );
+
+ }
+
+ //
+
+ this.scale = 1;
+
+ this.setSize = function( width, height ) {
+
+ renderer.setSize( width, height );
+
+ };
+
+ // fullscreen
+
+ var isFullscreen = false;
+
+ var canvas = renderer.domElement;
+ var fullscreenchange = canvas.mozRequestFullScreen ? 'mozfullscreenchange' : 'webkitfullscreenchange';
+
+ document.addEventListener( fullscreenchange, function ( event ) {
+
+ isFullscreen = document.mozFullScreenElement || document.webkitFullscreenElement;
+
+ }, false );
+
+ this.setFullScreen = function ( boolean ) {
+
+ if ( vrHMD === undefined ) return;
+ if ( isFullscreen === boolean ) return;
+
+ if ( canvas.mozRequestFullScreen ) {
+
+ canvas.mozRequestFullScreen( { vrDisplay: vrHMD } );
+
+ } else if ( canvas.webkitRequestFullscreen ) {
+
+ canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } );
+
+ }
+
+ };
+
+ // render
+
+ var cameraL = new THREE.PerspectiveCamera();
+ var cameraR = new THREE.PerspectiveCamera();
+
+ this.render = function ( scene, camera ) {
+
+ if ( vrHMD ) {
+
+ var eyeParamsL = vrHMD.getEyeParameters( 'left' );
+ var eyeParamsR = vrHMD.getEyeParameters( 'right' );
+
+ eyeTranslationL = eyeParamsL.eyeTranslation;
+ eyeTranslationR = eyeParamsR.eyeTranslation;
+ eyeFOVL = eyeParamsL.recommendedFieldOfView;
+ eyeFOVR = eyeParamsR.recommendedFieldOfView;
+ rectL = eyeParamsL.renderRect;
+ rectR = eyeParamsR.renderRect;
+
+ var sceneL, sceneR;
+
+ if ( Array.isArray( scene ) ) {
+
+ sceneL = scene[ 0 ];
+ sceneR = scene[ 1 ];
+
+ } else {
+
+ sceneL = scene;
+ sceneR = scene;
+
+ }
+
+ var size = renderer.getSize();
+ size.width /= 2;
+
+ renderer.enableScissorTest( true );
+ renderer.clear();
+
+ if ( camera.parent === null ) camera.updateMatrixWorld();
+
+ cameraL.projectionMatrix = fovToProjection( eyeFOVL, true, camera.near, camera.far );
+ cameraR.projectionMatrix = fovToProjection( eyeFOVR, true, camera.near, camera.far );
+
+ camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale );
+ camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale );
+
+ cameraL.translateX( eyeTranslationL.x * this.scale );
+ cameraR.translateX( eyeTranslationR.x * this.scale );
+
+ // render left eye
+ if ( rectL ) {
+
+ renderer.setViewport( rectL.x, rectL.y, rectL.width, rectL.height );
+ renderer.setScissor( rectL.x, rectL.y, rectL.width, rectL.height );
+
+ } else {
+
+ renderer.setViewport( 0, 0, size.width, size.height );
+ renderer.setScissor( 0, 0, size.width, size.height );
+
+ }
+ renderer.render( sceneL, cameraL );
+
+ // render right eye
+ if ( rectR ) {
+
+ renderer.setViewport( rectR.x, rectR.y, rectR.width, rectR.height );
+ renderer.setScissor( rectR.x, rectR.y, rectR.width, rectR.height );
+
+ } else {
+
+ renderer.setViewport( size.width, 0, size.width, size.height );
+ renderer.setScissor( size.width, 0, size.width, size.height );
+
+ }
+ renderer.render( sceneR, cameraR );
+
+ renderer.enableScissorTest( false );
+
+ return;
+
+ }
+
+ // Regular render mode if not HMD
+
+ if ( Array.isArray( scene ) ) scene = scene[ 0 ];
+
+ renderer.render( scene, camera );
+
+ };
+
+ //
+
+ function fovToNDCScaleOffset( fov ) {
+
+ var pxscale = 2.0 / ( fov.leftTan + fov.rightTan );
+ var pxoffset = ( fov.leftTan - fov.rightTan ) * pxscale * 0.5;
+ var pyscale = 2.0 / ( fov.upTan + fov.downTan );
+ var pyoffset = ( fov.upTan - fov.downTan ) * pyscale * 0.5;
+ return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] };
+
+ }
+
+ function fovPortToProjection( fov, rightHanded, zNear, zFar ) {
+
+ rightHanded = rightHanded === undefined ? true : rightHanded;
+ zNear = zNear === undefined ? 0.01 : zNear;
+ zFar = zFar === undefined ? 10000.0 : zFar;
+
+ var handednessScale = rightHanded ? - 1.0 : 1.0;
+
+ // start with an identity matrix
+ var mobj = new THREE.Matrix4();
+ var m = mobj.elements;
+
+ // and with scale/offset info for normalized device coords
+ var scaleAndOffset = fovToNDCScaleOffset( fov );
+
+ // X result, map clip edges to [-w,+w]
+ m[ 0 * 4 + 0 ] = scaleAndOffset.scale[ 0 ];
+ m[ 0 * 4 + 1 ] = 0.0;
+ m[ 0 * 4 + 2 ] = scaleAndOffset.offset[ 0 ] * handednessScale;
+ m[ 0 * 4 + 3 ] = 0.0;
+
+ // Y result, map clip edges to [-w,+w]
+ // Y offset is negated because this proj matrix transforms from world coords with Y=up,
+ // but the NDC scaling has Y=down (thanks D3D?)
+ m[ 1 * 4 + 0 ] = 0.0;
+ m[ 1 * 4 + 1 ] = scaleAndOffset.scale[ 1 ];
+ m[ 1 * 4 + 2 ] = - scaleAndOffset.offset[ 1 ] * handednessScale;
+ m[ 1 * 4 + 3 ] = 0.0;
+
+ // Z result (up to the app)
+ m[ 2 * 4 + 0 ] = 0.0;
+ m[ 2 * 4 + 1 ] = 0.0;
+ m[ 2 * 4 + 2 ] = zFar / ( zNear - zFar ) * - handednessScale;
+ m[ 2 * 4 + 3 ] = ( zFar * zNear ) / ( zNear - zFar );
+
+ // W result (= Z in)
+ m[ 3 * 4 + 0 ] = 0.0;
+ m[ 3 * 4 + 1 ] = 0.0;
+ m[ 3 * 4 + 2 ] = handednessScale;
+ m[ 3 * 4 + 3 ] = 0.0;
+
+ mobj.transpose();
+
+ return mobj;
+
+ }
+
+ function fovToProjection( fov, rightHanded, zNear, zFar ) {
+
+ var DEG2RAD = Math.PI / 180.0;
+
+ var fovPort = {
+ upTan: Math.tan( fov.upDegrees * DEG2RAD ),
+ downTan: Math.tan( fov.downDegrees * DEG2RAD ),
+ leftTan: Math.tan( fov.leftDegrees * DEG2RAD ),
+ rightTan: Math.tan( fov.rightDegrees * DEG2RAD )
+ };
+
+ return fovPortToProjection( fovPort, rightHanded, zNear, zFar );
+
+ }
+
+};
+
+try {
+ module.exports = VREffect;
+} catch (e) {
+ THREE.VREffect = VREffect;
+}
+
+},{}],5:[function(_dereq_,module,exports){
+var self = self || {};// File:src/Three.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+var THREE = { REVISION: '73' };
+
+//
+
+if ( typeof define === 'function' && define.amd ) {
+
+ define( 'three', THREE );
+
+} else if ( 'undefined' !== typeof exports && 'undefined' !== typeof module ) {
+
+ module.exports = THREE;
+
+}
+
+
+// polyfills
+
+if ( self.requestAnimationFrame === undefined || self.cancelAnimationFrame === undefined ) {
+
+ // Missing in Android stock browser.
+
+ ( function () {
+
+ var lastTime = 0;
+ var vendors = [ 'ms', 'moz', 'webkit', 'o' ];
+
+ for ( var x = 0; x < vendors.length && ! self.requestAnimationFrame; ++ x ) {
+
+ self.requestAnimationFrame = self[ vendors[ x ] + 'RequestAnimationFrame' ];
+ self.cancelAnimationFrame = self[ vendors[ x ] + 'CancelAnimationFrame' ] || self[ vendors[ x ] + 'CancelRequestAnimationFrame' ];
+
+ }
+
+ if ( self.requestAnimationFrame === undefined && self.setTimeout !== undefined ) {
+
+ self.requestAnimationFrame = function ( callback ) {
+
+ var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
+ var id = self.setTimeout( function () {
+
+ callback( currTime + timeToCall );
+
+ }, timeToCall );
+ lastTime = currTime + timeToCall;
+ return id;
+
+ };
+
+ }
+
+ if ( self.cancelAnimationFrame === undefined && self.clearTimeout !== undefined ) {
+
+ self.cancelAnimationFrame = function ( id ) {
+
+ self.clearTimeout( id );
+
+ };
+
+ }
+
+ } )();
+
+}
+
+//
+
+if ( self.performance === undefined ) {
+
+ self.performance = {};
+
+}
+
+if ( self.performance.now === undefined ) {
+
+ ( function () {
+
+ var start = Date.now();
+
+ self.performance.now = function () {
+
+ return Date.now() - start;
+
+ }
+
+ } )();
+
+}
+
+//
+
+if ( Number.EPSILON === undefined ) {
+
+ Number.EPSILON = Math.pow( 2, -52 );
+
+}
+
+//
+
+if ( Math.sign === undefined ) {
+
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
+
+ Math.sign = function ( x ) {
+
+ return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x;
+
+ };
+
+}
+
+if ( Function.prototype.name === undefined && Object.defineProperty !== undefined ) {
+
+ // Missing in IE9-11.
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
+
+ Object.defineProperty( Function.prototype, 'name', {
+
+ get: function () {
+
+ return this.toString().match( /^\s*function\s*(\S*)\s*\(/ )[ 1 ];
+
+ }
+
+ } );
+
+}
+
+// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.button
+
+THREE.MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
+
+// GL STATE CONSTANTS
+
+THREE.CullFaceNone = 0;
+THREE.CullFaceBack = 1;
+THREE.CullFaceFront = 2;
+THREE.CullFaceFrontBack = 3;
+
+THREE.FrontFaceDirectionCW = 0;
+THREE.FrontFaceDirectionCCW = 1;
+
+// SHADOWING TYPES
+
+THREE.BasicShadowMap = 0;
+THREE.PCFShadowMap = 1;
+THREE.PCFSoftShadowMap = 2;
+
+// MATERIAL CONSTANTS
+
+// side
+
+THREE.FrontSide = 0;
+THREE.BackSide = 1;
+THREE.DoubleSide = 2;
+
+// shading
+
+THREE.FlatShading = 1;
+THREE.SmoothShading = 2;
+
+// colors
+
+THREE.NoColors = 0;
+THREE.FaceColors = 1;
+THREE.VertexColors = 2;
+
+// blending modes
+
+THREE.NoBlending = 0;
+THREE.NormalBlending = 1;
+THREE.AdditiveBlending = 2;
+THREE.SubtractiveBlending = 3;
+THREE.MultiplyBlending = 4;
+THREE.CustomBlending = 5;
+
+// custom blending equations
+// (numbers start from 100 not to clash with other
+// mappings to OpenGL constants defined in Texture.js)
+
+THREE.AddEquation = 100;
+THREE.SubtractEquation = 101;
+THREE.ReverseSubtractEquation = 102;
+THREE.MinEquation = 103;
+THREE.MaxEquation = 104;
+
+// custom blending destination factors
+
+THREE.ZeroFactor = 200;
+THREE.OneFactor = 201;
+THREE.SrcColorFactor = 202;
+THREE.OneMinusSrcColorFactor = 203;
+THREE.SrcAlphaFactor = 204;
+THREE.OneMinusSrcAlphaFactor = 205;
+THREE.DstAlphaFactor = 206;
+THREE.OneMinusDstAlphaFactor = 207;
+
+// custom blending source factors
+
+//THREE.ZeroFactor = 200;
+//THREE.OneFactor = 201;
+//THREE.SrcAlphaFactor = 204;
+//THREE.OneMinusSrcAlphaFactor = 205;
+//THREE.DstAlphaFactor = 206;
+//THREE.OneMinusDstAlphaFactor = 207;
+THREE.DstColorFactor = 208;
+THREE.OneMinusDstColorFactor = 209;
+THREE.SrcAlphaSaturateFactor = 210;
+
+// depth modes
+
+THREE.NeverDepth = 0;
+THREE.AlwaysDepth = 1;
+THREE.LessDepth = 2;
+THREE.LessEqualDepth = 3;
+THREE.EqualDepth = 4;
+THREE.GreaterEqualDepth = 5;
+THREE.GreaterDepth = 6;
+THREE.NotEqualDepth = 7;
+
+
+// TEXTURE CONSTANTS
+
+THREE.MultiplyOperation = 0;
+THREE.MixOperation = 1;
+THREE.AddOperation = 2;
+
+// Mapping modes
+
+THREE.UVMapping = 300;
+
+THREE.CubeReflectionMapping = 301;
+THREE.CubeRefractionMapping = 302;
+
+THREE.EquirectangularReflectionMapping = 303;
+THREE.EquirectangularRefractionMapping = 304;
+
+THREE.SphericalReflectionMapping = 305;
+
+// Wrapping modes
+
+THREE.RepeatWrapping = 1000;
+THREE.ClampToEdgeWrapping = 1001;
+THREE.MirroredRepeatWrapping = 1002;
+
+// Filters
+
+THREE.NearestFilter = 1003;
+THREE.NearestMipMapNearestFilter = 1004;
+THREE.NearestMipMapLinearFilter = 1005;
+THREE.LinearFilter = 1006;
+THREE.LinearMipMapNearestFilter = 1007;
+THREE.LinearMipMapLinearFilter = 1008;
+
+// Data types
+
+THREE.UnsignedByteType = 1009;
+THREE.ByteType = 1010;
+THREE.ShortType = 1011;
+THREE.UnsignedShortType = 1012;
+THREE.IntType = 1013;
+THREE.UnsignedIntType = 1014;
+THREE.FloatType = 1015;
+THREE.HalfFloatType = 1025;
+
+// Pixel types
+
+//THREE.UnsignedByteType = 1009;
+THREE.UnsignedShort4444Type = 1016;
+THREE.UnsignedShort5551Type = 1017;
+THREE.UnsignedShort565Type = 1018;
+
+// Pixel formats
+
+THREE.AlphaFormat = 1019;
+THREE.RGBFormat = 1020;
+THREE.RGBAFormat = 1021;
+THREE.LuminanceFormat = 1022;
+THREE.LuminanceAlphaFormat = 1023;
+// THREE.RGBEFormat handled as THREE.RGBAFormat in shaders
+THREE.RGBEFormat = THREE.RGBAFormat; //1024;
+
+// DDS / ST3C Compressed texture formats
+
+THREE.RGB_S3TC_DXT1_Format = 2001;
+THREE.RGBA_S3TC_DXT1_Format = 2002;
+THREE.RGBA_S3TC_DXT3_Format = 2003;
+THREE.RGBA_S3TC_DXT5_Format = 2004;
+
+
+// PVRTC compressed texture formats
+
+THREE.RGB_PVRTC_4BPPV1_Format = 2100;
+THREE.RGB_PVRTC_2BPPV1_Format = 2101;
+THREE.RGBA_PVRTC_4BPPV1_Format = 2102;
+THREE.RGBA_PVRTC_2BPPV1_Format = 2103;
+
+// Loop styles for AnimationAction
+
+THREE.LoopOnce = 2200;
+THREE.LoopRepeat = 2201;
+THREE.LoopPingPong = 2202;
+
+// DEPRECATED
+
+THREE.Projector = function () {
+
+ console.error( 'THREE.Projector has been moved to /examples/js/renderers/Projector.js.' );
+
+ this.projectVector = function ( vector, camera ) {
+
+ console.warn( 'THREE.Projector: .projectVector() is now vector.project().' );
+ vector.project( camera );
+
+ };
+
+ this.unprojectVector = function ( vector, camera ) {
+
+ console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' );
+ vector.unproject( camera );
+
+ };
+
+ this.pickingRay = function ( vector, camera ) {
+
+ console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' );
+
+ };
+
+};
+
+THREE.CanvasRenderer = function () {
+
+ console.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' );
+
+ this.domElement = document.createElement( 'canvas' );
+ this.clear = function () {};
+ this.render = function () {};
+ this.setClearColor = function () {};
+ this.setSize = function () {};
+
+};
+
+// File:src/math/Color.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Color = function ( color ) {
+
+ if ( arguments.length === 3 ) {
+
+ return this.fromArray( arguments );
+
+ }
+
+ return this.set( color );
+
+};
+
+THREE.Color.prototype = {
+
+ constructor: THREE.Color,
+
+ r: 1, g: 1, b: 1,
+
+ set: function ( value ) {
+
+ if ( value instanceof THREE.Color ) {
+
+ this.copy( value );
+
+ } else if ( typeof value === 'number' ) {
+
+ this.setHex( value );
+
+ } else if ( typeof value === 'string' ) {
+
+ this.setStyle( value );
+
+ }
+
+ return this;
+
+ },
+
+ setHex: function ( hex ) {
+
+ hex = Math.floor( hex );
+
+ this.r = ( hex >> 16 & 255 ) / 255;
+ this.g = ( hex >> 8 & 255 ) / 255;
+ this.b = ( hex & 255 ) / 255;
+
+ return this;
+
+ },
+
+ setRGB: function ( r, g, b ) {
+
+ this.r = r;
+ this.g = g;
+ this.b = b;
+
+ return this;
+
+ },
+
+ setHSL: function () {
+
+ function hue2rgb( p, q, t ) {
+
+ if ( t < 0 ) t += 1;
+ if ( t > 1 ) t -= 1;
+ if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;
+ if ( t < 1 / 2 ) return q;
+ if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t );
+ return p;
+
+ }
+
+ return function ( h, s, l ) {
+
+ // h,s,l ranges are in 0.0 - 1.0
+ h = THREE.Math.euclideanModulo( h, 1 );
+ s = THREE.Math.clamp( s, 0, 1 );
+ l = THREE.Math.clamp( l, 0, 1 );
+
+ if ( s === 0 ) {
+
+ this.r = this.g = this.b = l;
+
+ } else {
+
+ var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );
+ var q = ( 2 * l ) - p;
+
+ this.r = hue2rgb( q, p, h + 1 / 3 );
+ this.g = hue2rgb( q, p, h );
+ this.b = hue2rgb( q, p, h - 1 / 3 );
+
+ }
+
+ return this;
+
+ };
+
+ }(),
+
+ setStyle: function ( style ) {
+
+ function handleAlpha( string ) {
+
+ if ( string === undefined ) return;
+
+ if ( parseFloat( string ) < 1 ) {
+
+ console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' );
+
+ }
+
+ }
+
+
+ var m;
+
+ if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) {
+
+ // rgb / hsl
+
+ var color;
+ var name = m[ 1 ];
+ var components = m[ 2 ];
+
+ switch ( name ) {
+
+ case 'rgb':
+ case 'rgba':
+
+ if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
+
+ // rgb(255,0,0) rgba(255,0,0,0.5)
+ this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255;
+ this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;
+ this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;
+
+ handleAlpha( color[ 5 ] );
+
+ return this;
+
+ }
+
+ if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
+
+ // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)
+ this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100;
+ this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;
+ this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;
+
+ handleAlpha( color[ 5 ] );
+
+ return this;
+
+ }
+
+ break;
+
+ case 'hsl':
+ case 'hsla':
+
+ if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
+
+ // hsl(120,50%,50%) hsla(120,50%,50%,0.5)
+ var h = parseFloat( color[ 1 ] ) / 360;
+ var s = parseInt( color[ 2 ], 10 ) / 100;
+ var l = parseInt( color[ 3 ], 10 ) / 100;
+
+ handleAlpha( color[ 5 ] );
+
+ return this.setHSL( h, s, l );
+
+ }
+
+ break;
+
+ }
+
+ } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) {
+
+ // hex color
+
+ var hex = m[ 1 ];
+ var size = hex.length;
+
+ if ( size === 3 ) {
+
+ // #ff0
+ this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255;
+ this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255;
+ this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255;
+
+ return this;
+
+ } else if ( size === 6 ) {
+
+ // #ff0000
+ this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255;
+ this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255;
+ this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255;
+
+ return this;
+
+ }
+
+ }
+
+ if ( style && style.length > 0 ) {
+
+ // color keywords
+ var hex = THREE.ColorKeywords[ style ];
+
+ if ( hex !== undefined ) {
+
+ // red
+ this.setHex( hex );
+
+ } else {
+
+ // unknown color
+ console.warn( 'THREE.Color: Unknown color ' + style );
+
+ }
+
+ }
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this.r, this.g, this.b );
+
+ },
+
+ copy: function ( color ) {
+
+ this.r = color.r;
+ this.g = color.g;
+ this.b = color.b;
+
+ return this;
+
+ },
+
+ copyGammaToLinear: function ( color, gammaFactor ) {
+
+ if ( gammaFactor === undefined ) gammaFactor = 2.0;
+
+ this.r = Math.pow( color.r, gammaFactor );
+ this.g = Math.pow( color.g, gammaFactor );
+ this.b = Math.pow( color.b, gammaFactor );
+
+ return this;
+
+ },
+
+ copyLinearToGamma: function ( color, gammaFactor ) {
+
+ if ( gammaFactor === undefined ) gammaFactor = 2.0;
+
+ var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0;
+
+ this.r = Math.pow( color.r, safeInverse );
+ this.g = Math.pow( color.g, safeInverse );
+ this.b = Math.pow( color.b, safeInverse );
+
+ return this;
+
+ },
+
+ convertGammaToLinear: function () {
+
+ var r = this.r, g = this.g, b = this.b;
+
+ this.r = r * r;
+ this.g = g * g;
+ this.b = b * b;
+
+ return this;
+
+ },
+
+ convertLinearToGamma: function () {
+
+ this.r = Math.sqrt( this.r );
+ this.g = Math.sqrt( this.g );
+ this.b = Math.sqrt( this.b );
+
+ return this;
+
+ },
+
+ getHex: function () {
+
+ return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;
+
+ },
+
+ getHexString: function () {
+
+ return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );
+
+ },
+
+ getHSL: function ( optionalTarget ) {
+
+ // h,s,l ranges are in 0.0 - 1.0
+
+ var hsl = optionalTarget || { h: 0, s: 0, l: 0 };
+
+ var r = this.r, g = this.g, b = this.b;
+
+ var max = Math.max( r, g, b );
+ var min = Math.min( r, g, b );
+
+ var hue, saturation;
+ var lightness = ( min + max ) / 2.0;
+
+ if ( min === max ) {
+
+ hue = 0;
+ saturation = 0;
+
+ } else {
+
+ var delta = max - min;
+
+ saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );
+
+ switch ( max ) {
+
+ case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;
+ case g: hue = ( b - r ) / delta + 2; break;
+ case b: hue = ( r - g ) / delta + 4; break;
+
+ }
+
+ hue /= 6;
+
+ }
+
+ hsl.h = hue;
+ hsl.s = saturation;
+ hsl.l = lightness;
+
+ return hsl;
+
+ },
+
+ getStyle: function () {
+
+ return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';
+
+ },
+
+ offsetHSL: function ( h, s, l ) {
+
+ var hsl = this.getHSL();
+
+ hsl.h += h; hsl.s += s; hsl.l += l;
+
+ this.setHSL( hsl.h, hsl.s, hsl.l );
+
+ return this;
+
+ },
+
+ add: function ( color ) {
+
+ this.r += color.r;
+ this.g += color.g;
+ this.b += color.b;
+
+ return this;
+
+ },
+
+ addColors: function ( color1, color2 ) {
+
+ this.r = color1.r + color2.r;
+ this.g = color1.g + color2.g;
+ this.b = color1.b + color2.b;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.r += s;
+ this.g += s;
+ this.b += s;
+
+ return this;
+
+ },
+
+ multiply: function ( color ) {
+
+ this.r *= color.r;
+ this.g *= color.g;
+ this.b *= color.b;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( s ) {
+
+ this.r *= s;
+ this.g *= s;
+ this.b *= s;
+
+ return this;
+
+ },
+
+ lerp: function ( color, alpha ) {
+
+ this.r += ( color.r - this.r ) * alpha;
+ this.g += ( color.g - this.g ) * alpha;
+ this.b += ( color.b - this.b ) * alpha;
+
+ return this;
+
+ },
+
+ equals: function ( c ) {
+
+ return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.r = array[ offset ];
+ this.g = array[ offset + 1 ];
+ this.b = array[ offset + 2 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.r;
+ array[ offset + 1 ] = this.g;
+ array[ offset + 2 ] = this.b;
+
+ return array;
+
+ }
+
+};
+
+THREE.ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,
+'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,
+'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,
+'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,
+'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,
+'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,
+'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,
+'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,
+'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,
+'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,
+'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,
+'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,
+'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,
+'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,
+'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,
+'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,
+'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,
+'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,
+'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,
+'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,
+'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,
+'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,
+'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,
+'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };
+
+// File:src/math/Quaternion.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Quaternion = function ( x, y, z, w ) {
+
+ this._x = x || 0;
+ this._y = y || 0;
+ this._z = z || 0;
+ this._w = ( w !== undefined ) ? w : 1;
+
+};
+
+THREE.Quaternion.prototype = {
+
+ constructor: THREE.Quaternion,
+
+ get x () {
+
+ return this._x;
+
+ },
+
+ set x ( value ) {
+
+ this._x = value;
+ this.onChangeCallback();
+
+ },
+
+ get y () {
+
+ return this._y;
+
+ },
+
+ set y ( value ) {
+
+ this._y = value;
+ this.onChangeCallback();
+
+ },
+
+ get z () {
+
+ return this._z;
+
+ },
+
+ set z ( value ) {
+
+ this._z = value;
+ this.onChangeCallback();
+
+ },
+
+ get w () {
+
+ return this._w;
+
+ },
+
+ set w ( value ) {
+
+ this._w = value;
+ this.onChangeCallback();
+
+ },
+
+ set: function ( x, y, z, w ) {
+
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._w = w;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this._x, this._y, this._z, this._w );
+
+ },
+
+ copy: function ( quaternion ) {
+
+ this._x = quaternion.x;
+ this._y = quaternion.y;
+ this._z = quaternion.z;
+ this._w = quaternion.w;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromEuler: function ( euler, update ) {
+
+ if ( euler instanceof THREE.Euler === false ) {
+
+ throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ // http://www.mathworks.com/matlabcentral/fileexchange/
+ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
+ // content/SpinCalc.m
+
+ var c1 = Math.cos( euler._x / 2 );
+ var c2 = Math.cos( euler._y / 2 );
+ var c3 = Math.cos( euler._z / 2 );
+ var s1 = Math.sin( euler._x / 2 );
+ var s2 = Math.sin( euler._y / 2 );
+ var s3 = Math.sin( euler._z / 2 );
+
+ var order = euler.order;
+
+ if ( order === 'XYZ' ) {
+
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if ( order === 'YXZ' ) {
+
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+
+ } else if ( order === 'ZXY' ) {
+
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if ( order === 'ZYX' ) {
+
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+
+ } else if ( order === 'YZX' ) {
+
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if ( order === 'XZY' ) {
+
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+
+ }
+
+ if ( update !== false ) this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromAxisAngle: function ( axis, angle ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
+
+ // assumes axis is normalized
+
+ var halfAngle = angle / 2, s = Math.sin( halfAngle );
+
+ this._x = axis.x * s;
+ this._y = axis.y * s;
+ this._z = axis.z * s;
+ this._w = Math.cos( halfAngle );
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromRotationMatrix: function ( m ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ var te = m.elements,
+
+ m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+ m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+ m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
+
+ trace = m11 + m22 + m33,
+ s;
+
+ if ( trace > 0 ) {
+
+ s = 0.5 / Math.sqrt( trace + 1.0 );
+
+ this._w = 0.25 / s;
+ this._x = ( m32 - m23 ) * s;
+ this._y = ( m13 - m31 ) * s;
+ this._z = ( m21 - m12 ) * s;
+
+ } else if ( m11 > m22 && m11 > m33 ) {
+
+ s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
+
+ this._w = ( m32 - m23 ) / s;
+ this._x = 0.25 * s;
+ this._y = ( m12 + m21 ) / s;
+ this._z = ( m13 + m31 ) / s;
+
+ } else if ( m22 > m33 ) {
+
+ s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
+
+ this._w = ( m13 - m31 ) / s;
+ this._x = ( m12 + m21 ) / s;
+ this._y = 0.25 * s;
+ this._z = ( m23 + m32 ) / s;
+
+ } else {
+
+ s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
+
+ this._w = ( m21 - m12 ) / s;
+ this._x = ( m13 + m31 ) / s;
+ this._y = ( m23 + m32 ) / s;
+ this._z = 0.25 * s;
+
+ }
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromUnitVectors: function () {
+
+ // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
+
+ // assumes direction vectors vFrom and vTo are normalized
+
+ var v1, r;
+
+ var EPS = 0.000001;
+
+ return function ( vFrom, vTo ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ r = vFrom.dot( vTo ) + 1;
+
+ if ( r < EPS ) {
+
+ r = 0;
+
+ if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
+
+ v1.set( - vFrom.y, vFrom.x, 0 );
+
+ } else {
+
+ v1.set( 0, - vFrom.z, vFrom.y );
+
+ }
+
+ } else {
+
+ v1.crossVectors( vFrom, vTo );
+
+ }
+
+ this._x = v1.x;
+ this._y = v1.y;
+ this._z = v1.z;
+ this._w = r;
+
+ this.normalize();
+
+ return this;
+
+ }
+
+ }(),
+
+ inverse: function () {
+
+ this.conjugate().normalize();
+
+ return this;
+
+ },
+
+ conjugate: function () {
+
+ this._x *= - 1;
+ this._y *= - 1;
+ this._z *= - 1;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
+
+ },
+
+ lengthSq: function () {
+
+ return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
+
+ },
+
+ normalize: function () {
+
+ var l = this.length();
+
+ if ( l === 0 ) {
+
+ this._x = 0;
+ this._y = 0;
+ this._z = 0;
+ this._w = 1;
+
+ } else {
+
+ l = 1 / l;
+
+ this._x = this._x * l;
+ this._y = this._y * l;
+ this._z = this._z * l;
+ this._w = this._w * l;
+
+ }
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ multiply: function ( q, p ) {
+
+ if ( p !== undefined ) {
+
+ console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
+ return this.multiplyQuaternions( q, p );
+
+ }
+
+ return this.multiplyQuaternions( this, q );
+
+ },
+
+ multiplyQuaternions: function ( a, b ) {
+
+ // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
+
+ var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
+ var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
+
+ this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
+ this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
+ this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
+ this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ multiplyVector3: function ( vector ) {
+
+ console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );
+ return vector.applyQuaternion( this );
+
+ },
+
+ slerp: function ( qb, t ) {
+
+ if ( t === 0 ) return this;
+ if ( t === 1 ) return this.copy( qb );
+
+ var x = this._x, y = this._y, z = this._z, w = this._w;
+
+ // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
+
+ var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
+
+ if ( cosHalfTheta < 0 ) {
+
+ this._w = - qb._w;
+ this._x = - qb._x;
+ this._y = - qb._y;
+ this._z = - qb._z;
+
+ cosHalfTheta = - cosHalfTheta;
+
+ } else {
+
+ this.copy( qb );
+
+ }
+
+ if ( cosHalfTheta >= 1.0 ) {
+
+ this._w = w;
+ this._x = x;
+ this._y = y;
+ this._z = z;
+
+ return this;
+
+ }
+
+ var halfTheta = Math.acos( cosHalfTheta );
+ var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
+
+ if ( Math.abs( sinHalfTheta ) < 0.001 ) {
+
+ this._w = 0.5 * ( w + this._w );
+ this._x = 0.5 * ( x + this._x );
+ this._y = 0.5 * ( y + this._y );
+ this._z = 0.5 * ( z + this._z );
+
+ return this;
+
+ }
+
+ var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
+ ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
+
+ this._w = ( w * ratioA + this._w * ratioB );
+ this._x = ( x * ratioA + this._x * ratioB );
+ this._y = ( y * ratioA + this._y * ratioB );
+ this._z = ( z * ratioA + this._z * ratioB );
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ equals: function ( quaternion ) {
+
+ return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this._x = array[ offset ];
+ this._y = array[ offset + 1 ];
+ this._z = array[ offset + 2 ];
+ this._w = array[ offset + 3 ];
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this._x;
+ array[ offset + 1 ] = this._y;
+ array[ offset + 2 ] = this._z;
+ array[ offset + 3 ] = this._w;
+
+ return array;
+
+ },
+
+ onChange: function ( callback ) {
+
+ this.onChangeCallback = callback;
+
+ return this;
+
+ },
+
+ onChangeCallback: function () {}
+
+};
+
+THREE.Quaternion.slerp = function ( qa, qb, qm, t ) {
+
+ return qm.copy( qa ).slerp( qb, t );
+
+};
+
+// File:src/math/Vector2.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author philogb / http://blog.thejit.org/
+ * @author egraether / http://egraether.com/
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ */
+
+THREE.Vector2 = function ( x, y ) {
+
+ this.x = x || 0;
+ this.y = y || 0;
+
+};
+
+THREE.Vector2.prototype = {
+
+ constructor: THREE.Vector2,
+
+ get width() { return this.x },
+ set width( value ) { this.x = value },
+
+ get height() { return this.y },
+ set height( value ) { this.y = value },
+
+ //
+
+ set: function ( x, y ) {
+
+ this.x = x;
+ this.y = y;
+
+ return this;
+
+ },
+
+ setX: function ( x ) {
+
+ this.x = x;
+
+ return this;
+
+ },
+
+ setY: function ( y ) {
+
+ this.y = y;
+
+ return this;
+
+ },
+
+ setComponent: function ( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ getComponent: function ( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this.x, this.y );
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+
+ return this;
+
+ },
+
+ addScaledVector: function ( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+
+ return this;
+
+ },
+
+ subScalar: function ( s ) {
+
+ this.x -= s;
+ this.y -= s;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+
+ return this;
+
+ },
+
+ multiply: function ( v ) {
+
+ this.x *= v.x;
+ this.y *= v.y;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( scalar ) {
+
+ if ( isFinite( scalar ) ) {
+ this.x *= scalar;
+ this.y *= scalar;
+ } else {
+ this.x = 0;
+ this.y = 0;
+ }
+
+ return this;
+
+ },
+
+ divide: function ( v ) {
+
+ this.x /= v.x;
+ this.y /= v.y;
+
+ return this;
+
+ },
+
+ divideScalar: function ( scalar ) {
+
+ return this.multiplyScalar( 1 / scalar );
+
+ },
+
+ min: function ( v ) {
+
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // This function assumes min < max, if this assumption isn't true it will not operate correctly
+
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+
+ return this;
+
+ },
+
+ clampScalar: function () {
+
+ var min, max;
+
+ return function clampScalar( minVal, maxVal ) {
+
+ if ( min === undefined ) {
+
+ min = new THREE.Vector2();
+ max = new THREE.Vector2();
+
+ }
+
+ min.set( minVal, minVal );
+ max.set( maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ }(),
+
+ clampLength: function ( min, max ) {
+
+ var length = this.length();
+
+ this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length );
+
+ return this;
+
+ },
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y;
+
+ },
+
+ lengthSq: function () {
+
+ return this.x * this.x + this.y * this.y;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this.x * this.x + this.y * this.y );
+
+ },
+
+ lengthManhattan: function() {
+
+ return Math.abs( this.x ) + Math.abs( this.y );
+
+ },
+
+ normalize: function () {
+
+ return this.divideScalar( this.length() );
+
+ },
+
+ distanceTo: function ( v ) {
+
+ return Math.sqrt( this.distanceToSquared( v ) );
+
+ },
+
+ distanceToSquared: function ( v ) {
+
+ var dx = this.x - v.x, dy = this.y - v.y;
+ return dx * dx + dy * dy;
+
+ },
+
+ setLength: function ( length ) {
+
+ return this.multiplyScalar( length / this.length() );
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+
+ return this;
+
+ },
+
+ lerpVectors: function ( v1, v2, alpha ) {
+
+ this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+ return this;
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+
+ return array;
+
+ },
+
+ fromAttribute: function ( attribute, index, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ index = index * attribute.itemSize + offset;
+
+ this.x = attribute.array[ index ];
+ this.y = attribute.array[ index + 1 ];
+
+ return this;
+
+ },
+
+ rotateAround: function ( center, angle ) {
+
+ var c = Math.cos( angle ), s = Math.sin( angle );
+
+ var x = this.x - center.x;
+ var y = this.y - center.y;
+
+ this.x = x * c - y * s + center.x;
+ this.y = x * s + y * c + center.y;
+
+ return this;
+
+ }
+
+};
+
+// File:src/math/Vector3.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author *kile / http://kile.stravaganza.org/
+ * @author philogb / http://blog.thejit.org/
+ * @author mikael emtinger / http://gomo.se/
+ * @author egraether / http://egraether.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.Vector3 = function ( x, y, z ) {
+
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+
+};
+
+THREE.Vector3.prototype = {
+
+ constructor: THREE.Vector3,
+
+ set: function ( x, y, z ) {
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+
+ return this;
+
+ },
+
+ setX: function ( x ) {
+
+ this.x = x;
+
+ return this;
+
+ },
+
+ setY: function ( y ) {
+
+ this.y = y;
+
+ return this;
+
+ },
+
+ setZ: function ( z ) {
+
+ this.z = z;
+
+ return this;
+
+ },
+
+ setComponent: function ( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ case 2: this.z = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ getComponent: function ( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ case 2: return this.z;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this.x, this.y, this.z );
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+ this.z += v.z;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+ this.z += s;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+ this.z = a.z + b.z;
+
+ return this;
+
+ },
+
+ addScaledVector: function ( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+ this.z += v.z * s;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+ this.z -= v.z;
+
+ return this;
+
+ },
+
+ subScalar: function ( s ) {
+
+ this.x -= s;
+ this.y -= s;
+ this.z -= s;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+ this.z = a.z - b.z;
+
+ return this;
+
+ },
+
+ multiply: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
+ return this.multiplyVectors( v, w );
+
+ }
+
+ this.x *= v.x;
+ this.y *= v.y;
+ this.z *= v.z;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( scalar ) {
+
+ if ( isFinite( scalar ) ) {
+ this.x *= scalar;
+ this.y *= scalar;
+ this.z *= scalar;
+ } else {
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ }
+
+ return this;
+
+ },
+
+ multiplyVectors: function ( a, b ) {
+
+ this.x = a.x * b.x;
+ this.y = a.y * b.y;
+ this.z = a.z * b.z;
+
+ return this;
+
+ },
+
+ applyEuler: function () {
+
+ var quaternion;
+
+ return function applyEuler( euler ) {
+
+ if ( euler instanceof THREE.Euler === false ) {
+
+ console.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
+
+ this.applyQuaternion( quaternion.setFromEuler( euler ) );
+
+ return this;
+
+ };
+
+ }(),
+
+ applyAxisAngle: function () {
+
+ var quaternion;
+
+ return function applyAxisAngle( axis, angle ) {
+
+ if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
+
+ this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
+
+ return this;
+
+ };
+
+ }(),
+
+ applyMatrix3: function ( m ) {
+
+ var x = this.x;
+ var y = this.y;
+ var z = this.z;
+
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
+ this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
+ this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
+
+ return this;
+
+ },
+
+ applyMatrix4: function ( m ) {
+
+ // input: THREE.Matrix4 affine matrix
+
+ var x = this.x, y = this.y, z = this.z;
+
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ];
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ];
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ];
+
+ return this;
+
+ },
+
+ applyProjection: function ( m ) {
+
+ // input: THREE.Matrix4 projection matrix
+
+ var x = this.x, y = this.y, z = this.z;
+
+ var e = m.elements;
+ var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide
+
+ this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d;
+ this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d;
+ this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d;
+
+ return this;
+
+ },
+
+ applyQuaternion: function ( q ) {
+
+ var x = this.x;
+ var y = this.y;
+ var z = this.z;
+
+ var qx = q.x;
+ var qy = q.y;
+ var qz = q.z;
+ var qw = q.w;
+
+ // calculate quat * vector
+
+ var ix = qw * x + qy * z - qz * y;
+ var iy = qw * y + qz * x - qx * z;
+ var iz = qw * z + qx * y - qy * x;
+ var iw = - qx * x - qy * y - qz * z;
+
+ // calculate result * inverse quat
+
+ this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
+ this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
+ this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
+
+ return this;
+
+ },
+
+ project: function () {
+
+ var matrix;
+
+ return function project( camera ) {
+
+ if ( matrix === undefined ) matrix = new THREE.Matrix4();
+
+ matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
+ return this.applyProjection( matrix );
+
+ };
+
+ }(),
+
+ unproject: function () {
+
+ var matrix;
+
+ return function unproject( camera ) {
+
+ if ( matrix === undefined ) matrix = new THREE.Matrix4();
+
+ matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
+ return this.applyProjection( matrix );
+
+ };
+
+ }(),
+
+ transformDirection: function ( m ) {
+
+ // input: THREE.Matrix4 affine matrix
+ // vector interpreted as a direction
+
+ var x = this.x, y = this.y, z = this.z;
+
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
+
+ this.normalize();
+
+ return this;
+
+ },
+
+ divide: function ( v ) {
+
+ this.x /= v.x;
+ this.y /= v.y;
+ this.z /= v.z;
+
+ return this;
+
+ },
+
+ divideScalar: function ( scalar ) {
+
+ return this.multiplyScalar( 1 / scalar );
+
+ },
+
+ min: function ( v ) {
+
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+ this.z = Math.min( this.z, v.z );
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+ this.z = Math.max( this.z, v.z );
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // This function assumes min < max, if this assumption isn't true it will not operate correctly
+
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+ this.z = Math.max( min.z, Math.min( max.z, this.z ) );
+
+ return this;
+
+ },
+
+ clampScalar: function () {
+
+ var min, max;
+
+ return function clampScalar( minVal, maxVal ) {
+
+ if ( min === undefined ) {
+
+ min = new THREE.Vector3();
+ max = new THREE.Vector3();
+
+ }
+
+ min.set( minVal, minVal, minVal );
+ max.set( maxVal, maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ }(),
+
+ clampLength: function ( min, max ) {
+
+ var length = this.length();
+
+ this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length );
+
+ return this;
+
+ },
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+ this.z = Math.floor( this.z );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+ this.z = Math.ceil( this.z );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+ this.z = Math.round( this.z );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+ this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+ this.z = - this.z;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y + this.z * v.z;
+
+ },
+
+ 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 );
+
+ },
+
+ lengthManhattan: function () {
+
+ return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
+
+ },
+
+ normalize: function () {
+
+ return this.divideScalar( this.length() );
+
+ },
+
+ setLength: function ( length ) {
+
+ return this.multiplyScalar( length / this.length() );
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+ this.z += ( v.z - this.z ) * alpha;
+
+ return this;
+
+ },
+
+ lerpVectors: function ( v1, v2, alpha ) {
+
+ this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+ return this;
+
+ },
+
+ cross: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
+ return this.crossVectors( v, w );
+
+ }
+
+ var x = this.x, y = this.y, z = this.z;
+
+ this.x = y * v.z - z * v.y;
+ this.y = z * v.x - x * v.z;
+ this.z = x * v.y - y * v.x;
+
+ return this;
+
+ },
+
+ crossVectors: function ( a, b ) {
+
+ var ax = a.x, ay = a.y, az = a.z;
+ var bx = b.x, by = b.y, bz = b.z;
+
+ this.x = ay * bz - az * by;
+ this.y = az * bx - ax * bz;
+ this.z = ax * by - ay * bx;
+
+ return this;
+
+ },
+
+ projectOnVector: function () {
+
+ var v1, dot;
+
+ return function projectOnVector( vector ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ v1.copy( vector ).normalize();
+
+ dot = this.dot( v1 );
+
+ return this.copy( v1 ).multiplyScalar( dot );
+
+ };
+
+ }(),
+
+ projectOnPlane: function () {
+
+ var v1;
+
+ return function projectOnPlane( planeNormal ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ v1.copy( this ).projectOnVector( planeNormal );
+
+ return this.sub( v1 );
+
+ }
+
+ }(),
+
+ reflect: function () {
+
+ // reflect incident vector off plane orthogonal to normal
+ // normal is assumed to have unit length
+
+ var v1;
+
+ return function reflect( normal ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
+
+ }
+
+ }(),
+
+ angleTo: function ( v ) {
+
+ var theta = this.dot( v ) / ( this.length() * v.length() );
+
+ // clamp, to handle numerical problems
+
+ return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) );
+
+ },
+
+ distanceTo: function ( v ) {
+
+ return Math.sqrt( this.distanceToSquared( v ) );
+
+ },
+
+ distanceToSquared: function ( v ) {
+
+ var dx = this.x - v.x;
+ var dy = this.y - v.y;
+ var dz = this.z - v.z;
+
+ return dx * dx + dy * dy + dz * dz;
+
+ },
+
+ setEulerFromRotationMatrix: function ( m, order ) {
+
+ console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' );
+
+ },
+
+ setEulerFromQuaternion: function ( q, order ) {
+
+ console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' );
+
+ },
+
+ getPositionFromMatrix: function ( m ) {
+
+ console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' );
+
+ return this.setFromMatrixPosition( m );
+
+ },
+
+ getScaleFromMatrix: function ( m ) {
+
+ console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' );
+
+ return this.setFromMatrixScale( m );
+
+ },
+
+ getColumnFromMatrix: function ( index, matrix ) {
+
+ console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' );
+
+ return this.setFromMatrixColumn( index, matrix );
+
+ },
+
+ setFromMatrixPosition: function ( m ) {
+
+ this.x = m.elements[ 12 ];
+ this.y = m.elements[ 13 ];
+ this.z = m.elements[ 14 ];
+
+ return this;
+
+ },
+
+ setFromMatrixScale: function ( m ) {
+
+ var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length();
+ var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length();
+ var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length();
+
+ this.x = sx;
+ this.y = sy;
+ this.z = sz;
+
+ return this;
+
+ },
+
+ setFromMatrixColumn: function ( index, matrix ) {
+
+ var offset = index * 4;
+
+ var me = matrix.elements;
+
+ this.x = me[ offset ];
+ this.y = me[ offset + 1 ];
+ this.z = me[ offset + 2 ];
+
+ return this;
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+ this.z = array[ offset + 2 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+ array[ offset + 2 ] = this.z;
+
+ return array;
+
+ },
+
+ fromAttribute: function ( attribute, index, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ index = index * attribute.itemSize + offset;
+
+ this.x = attribute.array[ index ];
+ this.y = attribute.array[ index + 1 ];
+ this.z = attribute.array[ index + 2 ];
+
+ return this;
+
+ }
+
+};
+
+// File:src/math/Vector4.js
+
+/**
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author philogb / http://blog.thejit.org/
+ * @author mikael emtinger / http://gomo.se/
+ * @author egraether / http://egraether.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.Vector4 = function ( x, y, z, w ) {
+
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+ this.w = ( w !== undefined ) ? w : 1;
+
+};
+
+THREE.Vector4.prototype = {
+
+ constructor: THREE.Vector4,
+
+ set: function ( x, y, z, w ) {
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+
+ return this;
+
+ },
+
+ setX: function ( x ) {
+
+ this.x = x;
+
+ return this;
+
+ },
+
+ setY: function ( y ) {
+
+ this.y = y;
+
+ return this;
+
+ },
+
+ setZ: function ( z ) {
+
+ this.z = z;
+
+ return this;
+
+ },
+
+ setW: function ( w ) {
+
+ this.w = w;
+
+ return this;
+
+ },
+
+ setComponent: function ( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ case 2: this.z = value; break;
+ case 3: this.w = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ getComponent: function ( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ case 2: return this.z;
+ case 3: return this.w;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this.x, this.y, this.z, this.w );
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+ this.w = ( v.w !== undefined ) ? v.w : 1;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+ this.z += v.z;
+ this.w += v.w;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+ this.z += s;
+ this.w += s;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+ this.z = a.z + b.z;
+ this.w = a.w + b.w;
+
+ return this;
+
+ },
+
+ addScaledVector: function ( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+ this.z += v.z * s;
+ this.w += v.w * s;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+ this.z -= v.z;
+ this.w -= v.w;
+
+ return this;
+
+ },
+
+ subScalar: function ( s ) {
+
+ this.x -= s;
+ this.y -= s;
+ this.z -= s;
+ this.w -= s;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+ this.z = a.z - b.z;
+ this.w = a.w - b.w;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( scalar ) {
+
+ if ( isFinite( scalar ) ) {
+ this.x *= scalar;
+ this.y *= scalar;
+ this.z *= scalar;
+ this.w *= scalar;
+ } else {
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ this.w = 0;
+ }
+
+ return this;
+
+ },
+
+ applyMatrix4: function ( m ) {
+
+ var x = this.x;
+ var y = this.y;
+ var z = this.z;
+ var w = this.w;
+
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;
+ this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;
+
+ return this;
+
+ },
+
+ divideScalar: function ( scalar ) {
+
+ return this.multiplyScalar( 1 / scalar );
+
+ },
+
+ setAxisAngleFromQuaternion: function ( q ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
+
+ // q is assumed to be normalized
+
+ this.w = 2 * Math.acos( q.w );
+
+ var s = Math.sqrt( 1 - q.w * q.w );
+
+ if ( s < 0.0001 ) {
+
+ this.x = 1;
+ this.y = 0;
+ this.z = 0;
+
+ } else {
+
+ this.x = q.x / s;
+ this.y = q.y / s;
+ this.z = q.z / s;
+
+ }
+
+ return this;
+
+ },
+
+ setAxisAngleFromRotationMatrix: function ( m ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ var angle, x, y, z, // variables for result
+ epsilon = 0.01, // margin to allow for rounding errors
+ epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees
+
+ te = m.elements,
+
+ m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+ m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+ m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
+
+ if ( ( Math.abs( m12 - m21 ) < epsilon )
+ && ( Math.abs( m13 - m31 ) < epsilon )
+ && ( Math.abs( m23 - m32 ) < epsilon ) ) {
+
+ // singularity found
+ // first check for identity matrix which must have +1 for all terms
+ // in leading diagonal and zero in other terms
+
+ if ( ( Math.abs( m12 + m21 ) < epsilon2 )
+ && ( Math.abs( m13 + m31 ) < epsilon2 )
+ && ( Math.abs( m23 + m32 ) < epsilon2 )
+ && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {
+
+ // this singularity is identity matrix so angle = 0
+
+ this.set( 1, 0, 0, 0 );
+
+ return this; // zero angle, arbitrary axis
+
+ }
+
+ // otherwise this singularity is angle = 180
+
+ angle = Math.PI;
+
+ var xx = ( m11 + 1 ) / 2;
+ var yy = ( m22 + 1 ) / 2;
+ var zz = ( m33 + 1 ) / 2;
+ var xy = ( m12 + m21 ) / 4;
+ var xz = ( m13 + m31 ) / 4;
+ var yz = ( m23 + m32 ) / 4;
+
+ if ( ( xx > yy ) && ( xx > zz ) ) {
+
+ // m11 is the largest diagonal term
+
+ if ( xx < epsilon ) {
+
+ x = 0;
+ y = 0.707106781;
+ z = 0.707106781;
+
+ } else {
+
+ x = Math.sqrt( xx );
+ y = xy / x;
+ z = xz / x;
+
+ }
+
+ } else if ( yy > zz ) {
+
+ // m22 is the largest diagonal term
+
+ if ( yy < epsilon ) {
+
+ x = 0.707106781;
+ y = 0;
+ z = 0.707106781;
+
+ } else {
+
+ y = Math.sqrt( yy );
+ x = xy / y;
+ z = yz / y;
+
+ }
+
+ } else {
+
+ // m33 is the largest diagonal term so base result on this
+
+ if ( zz < epsilon ) {
+
+ x = 0.707106781;
+ y = 0.707106781;
+ z = 0;
+
+ } else {
+
+ z = Math.sqrt( zz );
+ x = xz / z;
+ y = yz / z;
+
+ }
+
+ }
+
+ this.set( x, y, z, angle );
+
+ return this; // return 180 deg rotation
+
+ }
+
+ // as we have reached here there are no singularities so we can handle normally
+
+ var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 )
+ + ( m13 - m31 ) * ( m13 - m31 )
+ + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize
+
+ if ( Math.abs( s ) < 0.001 ) s = 1;
+
+ // prevent divide by zero, should not happen if matrix is orthogonal and should be
+ // caught by singularity test above, but I've left it in just in case
+
+ this.x = ( m32 - m23 ) / s;
+ this.y = ( m13 - m31 ) / s;
+ this.z = ( m21 - m12 ) / s;
+ this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );
+
+ return this;
+
+ },
+
+ min: function ( v ) {
+
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+ this.z = Math.min( this.z, v.z );
+ this.w = Math.min( this.w, v.w );
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+ this.z = Math.max( this.z, v.z );
+ this.w = Math.max( this.w, v.w );
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // This function assumes min < max, if this assumption isn't true it will not operate correctly
+
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+ this.z = Math.max( min.z, Math.min( max.z, this.z ) );
+ this.w = Math.max( min.w, Math.min( max.w, this.w ) );
+
+ return this;
+
+ },
+
+ clampScalar: function () {
+
+ var min, max;
+
+ return function clampScalar( minVal, maxVal ) {
+
+ if ( min === undefined ) {
+
+ min = new THREE.Vector4();
+ max = new THREE.Vector4();
+
+ }
+
+ min.set( minVal, minVal, minVal, minVal );
+ max.set( maxVal, maxVal, maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ }(),
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+ this.z = Math.floor( this.z );
+ this.w = Math.floor( this.w );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+ this.z = Math.ceil( this.z );
+ this.w = Math.ceil( this.w );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+ this.z = Math.round( this.z );
+ this.w = Math.round( this.w );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+ this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
+ this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+ this.z = - this.z;
+ this.w = - this.w;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;
+
+ },
+
+ lengthSq: function () {
+
+ return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );
+
+ },
+
+ lengthManhattan: 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() );
+
+ },
+
+ setLength: function ( length ) {
+
+ return this.multiplyScalar( length / this.length() );
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+ this.z += ( v.z - this.z ) * alpha;
+ this.w += ( v.w - this.w ) * alpha;
+
+ return this;
+
+ },
+
+ lerpVectors: function ( v1, v2, alpha ) {
+
+ this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+ return this;
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+ this.z = array[ offset + 2 ];
+ this.w = array[ offset + 3 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+ array[ offset + 2 ] = this.z;
+ array[ offset + 3 ] = this.w;
+
+ return array;
+
+ },
+
+ fromAttribute: function ( attribute, index, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ index = index * attribute.itemSize + offset;
+
+ this.x = attribute.array[ index ];
+ this.y = attribute.array[ index + 1 ];
+ this.z = attribute.array[ index + 2 ];
+ this.w = attribute.array[ index + 3 ];
+
+ return this;
+
+ }
+
+};
+
+// File:src/math/Euler.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Euler = function ( x, y, z, order ) {
+
+ this._x = x || 0;
+ this._y = y || 0;
+ this._z = z || 0;
+ this._order = order || THREE.Euler.DefaultOrder;
+
+};
+
+THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];
+
+THREE.Euler.DefaultOrder = 'XYZ';
+
+THREE.Euler.prototype = {
+
+ constructor: THREE.Euler,
+
+ get x () {
+
+ return this._x;
+
+ },
+
+ set x ( value ) {
+
+ this._x = value;
+ this.onChangeCallback();
+
+ },
+
+ get y () {
+
+ return this._y;
+
+ },
+
+ set y ( value ) {
+
+ this._y = value;
+ this.onChangeCallback();
+
+ },
+
+ get z () {
+
+ return this._z;
+
+ },
+
+ set z ( value ) {
+
+ this._z = value;
+ this.onChangeCallback();
+
+ },
+
+ get order () {
+
+ return this._order;
+
+ },
+
+ set order ( value ) {
+
+ this._order = value;
+ this.onChangeCallback();
+
+ },
+
+ set: function ( x, y, z, order ) {
+
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._order = order || this._order;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this._x, this._y, this._z, this._order);
+
+ },
+
+ copy: function ( euler ) {
+
+ this._x = euler._x;
+ this._y = euler._y;
+ this._z = euler._z;
+ this._order = euler._order;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromRotationMatrix: function ( m, order, update ) {
+
+ var clamp = THREE.Math.clamp;
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ var te = m.elements;
+ var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
+ var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
+ var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
+
+ order = order || this._order;
+
+ if ( order === 'XYZ' ) {
+
+ this._y = Math.asin( clamp( m13, - 1, 1 ) );
+
+ if ( Math.abs( m13 ) < 0.99999 ) {
+
+ this._x = Math.atan2( - m23, m33 );
+ this._z = Math.atan2( - m12, m11 );
+
+ } else {
+
+ this._x = Math.atan2( m32, m22 );
+ this._z = 0;
+
+ }
+
+ } else if ( order === 'YXZ' ) {
+
+ this._x = Math.asin( - clamp( m23, - 1, 1 ) );
+
+ if ( Math.abs( m23 ) < 0.99999 ) {
+
+ this._y = Math.atan2( m13, m33 );
+ this._z = Math.atan2( m21, m22 );
+
+ } else {
+
+ this._y = Math.atan2( - m31, m11 );
+ this._z = 0;
+
+ }
+
+ } else if ( order === 'ZXY' ) {
+
+ this._x = Math.asin( clamp( m32, - 1, 1 ) );
+
+ if ( Math.abs( m32 ) < 0.99999 ) {
+
+ this._y = Math.atan2( - m31, m33 );
+ this._z = Math.atan2( - m12, m22 );
+
+ } else {
+
+ this._y = 0;
+ this._z = Math.atan2( m21, m11 );
+
+ }
+
+ } else if ( order === 'ZYX' ) {
+
+ this._y = Math.asin( - clamp( m31, - 1, 1 ) );
+
+ if ( Math.abs( m31 ) < 0.99999 ) {
+
+ this._x = Math.atan2( m32, m33 );
+ this._z = Math.atan2( m21, m11 );
+
+ } else {
+
+ this._x = 0;
+ this._z = Math.atan2( - m12, m22 );
+
+ }
+
+ } else if ( order === 'YZX' ) {
+
+ this._z = Math.asin( clamp( m21, - 1, 1 ) );
+
+ if ( Math.abs( m21 ) < 0.99999 ) {
+
+ this._x = Math.atan2( - m23, m22 );
+ this._y = Math.atan2( - m31, m11 );
+
+ } else {
+
+ this._x = 0;
+ this._y = Math.atan2( m13, m33 );
+
+ }
+
+ } else if ( order === 'XZY' ) {
+
+ this._z = Math.asin( - clamp( m12, - 1, 1 ) );
+
+ if ( Math.abs( m12 ) < 0.99999 ) {
+
+ this._x = Math.atan2( m32, m22 );
+ this._y = Math.atan2( m13, m11 );
+
+ } else {
+
+ this._x = Math.atan2( - m23, m33 );
+ this._y = 0;
+
+ }
+
+ } else {
+
+ console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order )
+
+ }
+
+ this._order = order;
+
+ if ( update !== false ) this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromQuaternion: function () {
+
+ var matrix;
+
+ return function ( q, order, update ) {
+
+ if ( matrix === undefined ) matrix = new THREE.Matrix4();
+ matrix.makeRotationFromQuaternion( q );
+ this.setFromRotationMatrix( matrix, order, update );
+
+ return this;
+
+ };
+
+ }(),
+
+ setFromVector3: function ( v, order ) {
+
+ return this.set( v.x, v.y, v.z, order || this._order );
+
+ },
+
+ reorder: function () {
+
+ // WARNING: this discards revolution information -bhouston
+
+ var q = new THREE.Quaternion();
+
+ return function ( newOrder ) {
+
+ q.setFromEuler( this );
+ this.setFromQuaternion( q, newOrder );
+
+ };
+
+ }(),
+
+ equals: function ( euler ) {
+
+ return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
+
+ },
+
+ fromArray: function ( array ) {
+
+ this._x = array[ 0 ];
+ this._y = array[ 1 ];
+ this._z = array[ 2 ];
+ if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this._x;
+ array[ offset + 1 ] = this._y;
+ array[ offset + 2 ] = this._z;
+ array[ offset + 3 ] = this._order;
+
+ return array;
+
+ },
+
+ toVector3: function ( optionalResult ) {
+
+ if ( optionalResult ) {
+
+ return optionalResult.set( this._x, this._y, this._z );
+
+ } else {
+
+ return new THREE.Vector3( this._x, this._y, this._z );
+
+ }
+
+ },
+
+ onChange: function ( callback ) {
+
+ this.onChangeCallback = callback;
+
+ return this;
+
+ },
+
+ onChangeCallback: function () {}
+
+};
+
+// File:src/math/Line3.js
+
+/**
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Line3 = function ( start, end ) {
+
+ this.start = ( start !== undefined ) ? start : new THREE.Vector3();
+ this.end = ( end !== undefined ) ? end : new THREE.Vector3();
+
+};
+
+THREE.Line3.prototype = {
+
+ constructor: THREE.Line3,
+
+ set: function ( start, end ) {
+
+ this.start.copy( start );
+ this.end.copy( end );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( line ) {
+
+ this.start.copy( line.start );
+ this.end.copy( line.end );
+
+ return this;
+
+ },
+
+ center: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 );
+
+ },
+
+ delta: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.subVectors( this.end, this.start );
+
+ },
+
+ distanceSq: function () {
+
+ return this.start.distanceToSquared( this.end );
+
+ },
+
+ distance: function () {
+
+ return this.start.distanceTo( this.end );
+
+ },
+
+ at: function ( t, optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ return this.delta( result ).multiplyScalar( t ).add( this.start );
+
+ },
+
+ closestPointToPointParameter: function () {
+
+ var startP = new THREE.Vector3();
+ var startEnd = new THREE.Vector3();
+
+ return function ( point, clampToLine ) {
+
+ startP.subVectors( point, this.start );
+ startEnd.subVectors( this.end, this.start );
+
+ var startEnd2 = startEnd.dot( startEnd );
+ var startEnd_startP = startEnd.dot( startP );
+
+ var t = startEnd_startP / startEnd2;
+
+ if ( clampToLine ) {
+
+ t = THREE.Math.clamp( t, 0, 1 );
+
+ }
+
+ return t;
+
+ };
+
+ }(),
+
+ closestPointToPoint: function ( point, clampToLine, optionalTarget ) {
+
+ var t = this.closestPointToPointParameter( point, clampToLine );
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ return this.delta( result ).multiplyScalar( t ).add( this.start );
+
+ },
+
+ applyMatrix4: function ( matrix ) {
+
+ this.start.applyMatrix4( matrix );
+ this.end.applyMatrix4( matrix );
+
+ return this;
+
+ },
+
+ equals: function ( line ) {
+
+ return line.start.equals( this.start ) && line.end.equals( this.end );
+
+ }
+
+};
+
+// File:src/math/Box2.js
+
+/**
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Box2 = function ( min, max ) {
+
+ this.min = ( min !== undefined ) ? min : new THREE.Vector2( Infinity, Infinity );
+ this.max = ( max !== undefined ) ? max : new THREE.Vector2( - Infinity, - Infinity );
+
+};
+
+THREE.Box2.prototype = {
+
+ constructor: THREE.Box2,
+
+ set: function ( min, max ) {
+
+ this.min.copy( min );
+ this.max.copy( max );
+
+ return this;
+
+ },
+
+ setFromPoints: function ( points ) {
+
+ this.makeEmpty();
+
+ for ( var i = 0, il = points.length; i < il; i ++ ) {
+
+ this.expandByPoint( points[ i ] )
+
+ }
+
+ return this;
+
+ },
+
+ setFromCenterAndSize: function () {
+
+ var v1 = new THREE.Vector2();
+
+ return function ( center, size ) {
+
+ var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
+ this.min.copy( center ).sub( halfSize );
+ this.max.copy( center ).add( halfSize );
+
+ return this;
+
+ };
+
+ }(),
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( box ) {
+
+ this.min.copy( box.min );
+ this.max.copy( box.max );
+
+ return this;
+
+ },
+
+ makeEmpty: function () {
+
+ this.min.x = this.min.y = Infinity;
+ this.max.x = this.max.y = - Infinity;
+
+ return this;
+
+ },
+
+ empty: function () {
+
+ // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
+
+ return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y );
+
+ },
+
+ center: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector2();
+ return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
+
+ },
+
+ size: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector2();
+ return result.subVectors( this.max, this.min );
+
+ },
+
+ expandByPoint: function ( point ) {
+
+ this.min.min( point );
+ this.max.max( point );
+
+ return this;
+
+ },
+
+ expandByVector: function ( vector ) {
+
+ this.min.sub( vector );
+ this.max.add( vector );
+
+ return this;
+
+ },
+
+ expandByScalar: function ( scalar ) {
+
+ this.min.addScalar( - scalar );
+ this.max.addScalar( scalar );
+
+ return this;
+
+ },
+
+ containsPoint: function ( point ) {
+
+ if ( point.x < this.min.x || point.x > this.max.x ||
+ point.y < this.min.y || point.y > this.max.y ) {
+
+ return false;
+
+ }
+
+ return true;
+
+ },
+
+ containsBox: function ( box ) {
+
+ if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&
+ ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) ) {
+
+ return true;
+
+ }
+
+ return false;
+
+ },
+
+ getParameter: function ( point, optionalTarget ) {
+
+ // This can potentially have a divide by zero if the box
+ // has a size dimension of 0.
+
+ var result = optionalTarget || new THREE.Vector2();
+
+ return result.set(
+ ( point.x - this.min.x ) / ( this.max.x - this.min.x ),
+ ( point.y - this.min.y ) / ( this.max.y - this.min.y )
+ );
+
+ },
+
+ isIntersectionBox: function ( box ) {
+
+ // using 6 splitting planes to rule out intersections.
+
+ if ( box.max.x < this.min.x || box.min.x > this.max.x ||
+ box.max.y < this.min.y || box.min.y > this.max.y ) {
+
+ return false;
+
+ }
+
+ return true;
+
+ },
+
+ clampPoint: function ( point, optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector2();
+ return result.copy( point ).clamp( this.min, this.max );
+
+ },
+
+ distanceToPoint: function () {
+
+ var v1 = new THREE.Vector2();
+
+ return function ( point ) {
+
+ var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
+ return clampedPoint.sub( point ).length();
+
+ };
+
+ }(),
+
+ intersect: function ( box ) {
+
+ this.min.max( box.min );
+ this.max.min( box.max );
+
+ return this;
+
+ },
+
+ union: function ( box ) {
+
+ this.min.min( box.min );
+ this.max.max( box.max );
+
+ return this;
+
+ },
+
+ translate: function ( offset ) {
+
+ this.min.add( offset );
+ this.max.add( offset );
+
+ return this;
+
+ },
+
+ equals: function ( box ) {
+
+ return box.min.equals( this.min ) && box.max.equals( this.max );
+
+ }
+
+};
+
+// File:src/math/Box3.js
+
+/**
+ * @author bhouston / http://clara.io
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.Box3 = function ( min, max ) {
+
+ this.min = ( min !== undefined ) ? min : new THREE.Vector3( Infinity, Infinity, Infinity );
+ this.max = ( max !== undefined ) ? max : new THREE.Vector3( - Infinity, - Infinity, - Infinity );
+
+};
+
+THREE.Box3.prototype = {
+
+ constructor: THREE.Box3,
+
+ set: function ( min, max ) {
+
+ this.min.copy( min );
+ this.max.copy( max );
+
+ return this;
+
+ },
+
+ setFromPoints: function ( points ) {
+
+ this.makeEmpty();
+
+ for ( var i = 0, il = points.length; i < il; i ++ ) {
+
+ this.expandByPoint( points[ i ] );
+
+ }
+
+ return this;
+
+ },
+
+ setFromCenterAndSize: function () {
+
+ var v1 = new THREE.Vector3();
+
+ return function ( center, size ) {
+
+ var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
+
+ this.min.copy( center ).sub( halfSize );
+ this.max.copy( center ).add( halfSize );
+
+ return this;
+
+ };
+
+ }(),
+
+ setFromObject: function () {
+
+ // Computes the world-axis-aligned bounding box of an object (including its children),
+ // accounting for both the object's, and children's, world transforms
+
+ var v1 = new THREE.Vector3();
+
+ return function ( object ) {
+
+ var scope = this;
+
+ object.updateMatrixWorld( true );
+
+ this.makeEmpty();
+
+ object.traverse( function ( node ) {
+
+ var geometry = node.geometry;
+
+ if ( geometry !== undefined ) {
+
+ if ( geometry instanceof THREE.Geometry ) {
+
+ var vertices = geometry.vertices;
+
+ for ( var i = 0, il = vertices.length; i < il; i ++ ) {
+
+ v1.copy( vertices[ i ] );
+
+ v1.applyMatrix4( node.matrixWorld );
+
+ scope.expandByPoint( v1 );
+
+ }
+
+ } else if ( geometry instanceof THREE.BufferGeometry && geometry.attributes[ 'position' ] !== undefined ) {
+
+ var positions = geometry.attributes[ 'position' ].array;
+
+ for ( var i = 0, il = positions.length; i < il; i += 3 ) {
+
+ v1.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
+
+ v1.applyMatrix4( node.matrixWorld );
+
+ scope.expandByPoint( v1 );
+
+ }
+
+ }
+
+ }
+
+ } );
+
+ return this;
+
+ };
+
+ }(),
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( box ) {
+
+ this.min.copy( box.min );
+ this.max.copy( box.max );
+
+ return this;
+
+ },
+
+ makeEmpty: function () {
+
+ this.min.x = this.min.y = this.min.z = Infinity;
+ this.max.x = this.max.y = this.max.z = - Infinity;
+
+ return this;
+
+ },
+
+ empty: function () {
+
+ // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
+
+ return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );
+
+ },
+
+ center: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
+
+ },
+
+ size: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.subVectors( this.max, this.min );
+
+ },
+
+ expandByPoint: function ( point ) {
+
+ this.min.min( point );
+ this.max.max( point );
+
+ return this;
+
+ },
+
+ expandByVector: function ( vector ) {
+
+ this.min.sub( vector );
+ this.max.add( vector );
+
+ return this;
+
+ },
+
+ expandByScalar: function ( scalar ) {
+
+ this.min.addScalar( - scalar );
+ this.max.addScalar( scalar );
+
+ return this;
+
+ },
+
+ containsPoint: function ( point ) {
+
+ if ( point.x < this.min.x || point.x > this.max.x ||
+ point.y < this.min.y || point.y > this.max.y ||
+ point.z < this.min.z || point.z > this.max.z ) {
+
+ return false;
+
+ }
+
+ return true;
+
+ },
+
+ containsBox: function ( box ) {
+
+ if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&
+ ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) &&
+ ( this.min.z <= box.min.z ) && ( box.max.z <= this.max.z ) ) {
+
+ return true;
+
+ }
+
+ return false;
+
+ },
+
+ getParameter: function ( point, optionalTarget ) {
+
+ // This can potentially have a divide by zero if the box
+ // has a size dimension of 0.
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ return result.set(
+ ( point.x - this.min.x ) / ( this.max.x - this.min.x ),
+ ( point.y - this.min.y ) / ( this.max.y - this.min.y ),
+ ( point.z - this.min.z ) / ( this.max.z - this.min.z )
+ );
+
+ },
+
+ isIntersectionBox: function ( box ) {
+
+ // using 6 splitting planes to rule out intersections.
+
+ if ( box.max.x < this.min.x || box.min.x > this.max.x ||
+ box.max.y < this.min.y || box.min.y > this.max.y ||
+ box.max.z < this.min.z || box.min.z > this.max.z ) {
+
+ return false;
+
+ }
+
+ return true;
+
+ },
+
+ clampPoint: function ( point, optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.copy( point ).clamp( this.min, this.max );
+
+ },
+
+ distanceToPoint: function () {
+
+ var v1 = new THREE.Vector3();
+
+ return function ( point ) {
+
+ var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
+ return clampedPoint.sub( point ).length();
+
+ };
+
+ }(),
+
+ getBoundingSphere: function () {
+
+ var v1 = new THREE.Vector3();
+
+ return function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Sphere();
+
+ result.center = this.center();
+ result.radius = this.size( v1 ).length() * 0.5;
+
+ return result;
+
+ };
+
+ }(),
+
+ intersect: function ( box ) {
+
+ this.min.max( box.min );
+ this.max.min( box.max );
+
+ return this;
+
+ },
+
+ union: function ( box ) {
+
+ this.min.min( box.min );
+ this.max.max( box.max );
+
+ return this;
+
+ },
+
+ applyMatrix4: function () {
+
+ var points = [
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ new THREE.Vector3()
+ ];
+
+ return function ( matrix ) {
+
+ // NOTE: I am using a binary pattern to specify all 2^3 combinations below
+ points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000
+ points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001
+ points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010
+ points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011
+ points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100
+ points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101
+ points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110
+ points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111
+
+ this.makeEmpty();
+ this.setFromPoints( points );
+
+ return this;
+
+ };
+
+ }(),
+
+ translate: function ( offset ) {
+
+ this.min.add( offset );
+ this.max.add( offset );
+
+ return this;
+
+ },
+
+ equals: function ( box ) {
+
+ return box.min.equals( this.min ) && box.max.equals( this.max );
+
+ }
+
+};
+
+// File:src/math/Matrix3.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Matrix3 = function () {
+
+ this.elements = new Float32Array( [
+
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
+
+ ] );
+
+ if ( arguments.length > 0 ) {
+
+ console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' );
+
+ }
+
+};
+
+THREE.Matrix3.prototype = {
+
+ constructor: THREE.Matrix3,
+
+ set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
+
+ var te = this.elements;
+
+ te[ 0 ] = n11; te[ 3 ] = n12; te[ 6 ] = n13;
+ te[ 1 ] = n21; te[ 4 ] = n22; te[ 7 ] = n23;
+ te[ 2 ] = n31; te[ 5 ] = n32; te[ 8 ] = n33;
+
+ return this;
+
+ },
+
+ identity: function () {
+
+ this.set(
+
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().fromArray( this.elements );
+
+ },
+
+ copy: function ( m ) {
+
+ var me = m.elements;
+
+ this.set(
+
+ me[ 0 ], me[ 3 ], me[ 6 ],
+ me[ 1 ], me[ 4 ], me[ 7 ],
+ me[ 2 ], me[ 5 ], me[ 8 ]
+
+ );
+
+ return this;
+
+ },
+
+ multiplyVector3: function ( vector ) {
+
+ console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' );
+ return vector.applyMatrix3( this );
+
+ },
+
+ multiplyVector3Array: function ( a ) {
+
+ console.warn( 'THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' );
+ return this.applyToVector3Array( a );
+
+ },
+
+ applyToVector3Array: function () {
+
+ var v1;
+
+ return function ( array, offset, length ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+ if ( offset === undefined ) offset = 0;
+ if ( length === undefined ) length = array.length;
+
+ for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) {
+
+ v1.fromArray( array, j );
+ v1.applyMatrix3( this );
+ v1.toArray( array, j );
+
+ }
+
+ return array;
+
+ };
+
+ }(),
+
+ applyToBuffer: function () {
+
+ var v1;
+
+ return function applyToBuffer( buffer, offset, length ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+ if ( offset === undefined ) offset = 0;
+ if ( length === undefined ) length = buffer.length / buffer.itemSize;
+
+ for ( var i = 0, j = offset; i < length; i ++, j ++ ) {
+
+ v1.x = buffer.getX( j );
+ v1.y = buffer.getY( j );
+ v1.z = buffer.getZ( j );
+
+ v1.applyMatrix3( this );
+
+ buffer.setXYZ( v1.x, v1.y, v1.z );
+
+ }
+
+ return buffer;
+
+ };
+
+ }(),
+
+ multiplyScalar: function ( s ) {
+
+ var te = this.elements;
+
+ te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
+ te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
+ te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;
+
+ return this;
+
+ },
+
+ determinant: function () {
+
+ var te = this.elements;
+
+ var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
+ d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
+ g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];
+
+ return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;
+
+ },
+
+ getInverse: function ( matrix, throwOnInvertible ) {
+
+ // input: THREE.Matrix4
+ // ( based on http://code.google.com/p/webgl-mjs/ )
+
+ var me = matrix.elements;
+ var te = this.elements;
+
+ te[ 0 ] = me[ 10 ] * me[ 5 ] - me[ 6 ] * me[ 9 ];
+ te[ 1 ] = - me[ 10 ] * me[ 1 ] + me[ 2 ] * me[ 9 ];
+ te[ 2 ] = me[ 6 ] * me[ 1 ] - me[ 2 ] * me[ 5 ];
+ te[ 3 ] = - me[ 10 ] * me[ 4 ] + me[ 6 ] * me[ 8 ];
+ te[ 4 ] = me[ 10 ] * me[ 0 ] - me[ 2 ] * me[ 8 ];
+ te[ 5 ] = - me[ 6 ] * me[ 0 ] + me[ 2 ] * me[ 4 ];
+ te[ 6 ] = me[ 9 ] * me[ 4 ] - me[ 5 ] * me[ 8 ];
+ te[ 7 ] = - me[ 9 ] * me[ 0 ] + me[ 1 ] * me[ 8 ];
+ te[ 8 ] = me[ 5 ] * me[ 0 ] - me[ 1 ] * me[ 4 ];
+
+ var det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 3 ] + me[ 2 ] * te[ 6 ];
+
+ // no inverse
+
+ if ( det === 0 ) {
+
+ var msg = "Matrix3.getInverse(): can't invert matrix, determinant is 0";
+
+ if ( throwOnInvertible || false ) {
+
+ throw new Error( msg );
+
+ } else {
+
+ console.warn( msg );
+
+ }
+
+ this.identity();
+
+ return this;
+
+ }
+
+ this.multiplyScalar( 1.0 / det );
+
+ return this;
+
+ },
+
+ transpose: function () {
+
+ var tmp, m = this.elements;
+
+ tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
+ tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
+ tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;
+
+ return this;
+
+ },
+
+ flattenToArrayOffset: function ( array, offset ) {
+
+ var te = this.elements;
+
+ array[ offset ] = te[ 0 ];
+ array[ offset + 1 ] = te[ 1 ];
+ array[ offset + 2 ] = te[ 2 ];
+
+ array[ offset + 3 ] = te[ 3 ];
+ array[ offset + 4 ] = te[ 4 ];
+ array[ offset + 5 ] = te[ 5 ];
+
+ array[ offset + 6 ] = te[ 6 ];
+ array[ offset + 7 ] = te[ 7 ];
+ array[ offset + 8 ] = te[ 8 ];
+
+ return array;
+
+ },
+
+ getNormalMatrix: function ( m ) {
+
+ // input: THREE.Matrix4
+
+ this.getInverse( m ).transpose();
+
+ return this;
+
+ },
+
+ transposeIntoArray: function ( r ) {
+
+ var m = this.elements;
+
+ r[ 0 ] = m[ 0 ];
+ r[ 1 ] = m[ 3 ];
+ r[ 2 ] = m[ 6 ];
+ r[ 3 ] = m[ 1 ];
+ r[ 4 ] = m[ 4 ];
+ r[ 5 ] = m[ 7 ];
+ r[ 6 ] = m[ 2 ];
+ r[ 7 ] = m[ 5 ];
+ r[ 8 ] = m[ 8 ];
+
+ return this;
+
+ },
+
+ fromArray: function ( array ) {
+
+ this.elements.set( array );
+
+ return this;
+
+ },
+
+ toArray: function () {
+
+ var te = this.elements;
+
+ return [
+ te[ 0 ], te[ 1 ], te[ 2 ],
+ te[ 3 ], te[ 4 ], te[ 5 ],
+ te[ 6 ], te[ 7 ], te[ 8 ]
+ ];
+
+ }
+
+};
+
+// File:src/math/Matrix4.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author philogb / http://blog.thejit.org/
+ * @author jordi_ros / http://plattsoft.com
+ * @author D1plo1d / http://github.com/D1plo1d
+ * @author alteredq / http://alteredqualia.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author timknip / http://www.floorplanner.com/
+ * @author bhouston / http://clara.io
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.Matrix4 = function () {
+
+ this.elements = new Float32Array( [
+
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+
+ ] );
+
+ if ( arguments.length > 0 ) {
+
+ console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );
+
+ }
+
+};
+
+THREE.Matrix4.prototype = {
+
+ constructor: THREE.Matrix4,
+
+ set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
+
+ var te = this.elements;
+
+ te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
+ te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
+ te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
+ te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;
+
+ return this;
+
+ },
+
+ identity: function () {
+
+ this.set(
+
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new THREE.Matrix4().fromArray( this.elements );
+
+ },
+
+ copy: function ( m ) {
+
+ this.elements.set( m.elements );
+
+ return this;
+
+ },
+
+ extractPosition: function ( m ) {
+
+ console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' );
+ return this.copyPosition( m );
+
+ },
+
+ copyPosition: function ( m ) {
+
+ var te = this.elements;
+ var me = m.elements;
+
+ te[ 12 ] = me[ 12 ];
+ te[ 13 ] = me[ 13 ];
+ te[ 14 ] = me[ 14 ];
+
+ return this;
+
+ },
+
+ extractBasis: function ( xAxis, yAxis, zAxis ) {
+
+ var te = this.elements;
+
+ xAxis.set( te[ 0 ], te[ 1 ], te[ 2 ] );
+ yAxis.set( te[ 4 ], te[ 5 ], te[ 6 ] );
+ zAxis.set( te[ 8 ], te[ 9 ], te[ 10 ] );
+
+ return this;
+
+ },
+
+ makeBasis: function ( xAxis, yAxis, zAxis ) {
+
+ this.set(
+ xAxis.x, yAxis.x, zAxis.x, 0,
+ xAxis.y, yAxis.y, zAxis.y, 0,
+ xAxis.z, yAxis.z, zAxis.z, 0,
+ 0, 0, 0, 1
+ );
+
+ return this;
+
+ },
+
+ extractRotation: function () {
+
+ var v1;
+
+ return function ( m ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ var te = this.elements;
+ var me = m.elements;
+
+ var scaleX = 1 / v1.set( me[ 0 ], me[ 1 ], me[ 2 ] ).length();
+ var scaleY = 1 / v1.set( me[ 4 ], me[ 5 ], me[ 6 ] ).length();
+ var scaleZ = 1 / v1.set( me[ 8 ], me[ 9 ], me[ 10 ] ).length();
+
+ te[ 0 ] = me[ 0 ] * scaleX;
+ te[ 1 ] = me[ 1 ] * scaleX;
+ te[ 2 ] = me[ 2 ] * scaleX;
+
+ te[ 4 ] = me[ 4 ] * scaleY;
+ te[ 5 ] = me[ 5 ] * scaleY;
+ te[ 6 ] = me[ 6 ] * scaleY;
+
+ te[ 8 ] = me[ 8 ] * scaleZ;
+ te[ 9 ] = me[ 9 ] * scaleZ;
+ te[ 10 ] = me[ 10 ] * scaleZ;
+
+ return this;
+
+ };
+
+ }(),
+
+ makeRotationFromEuler: function ( euler ) {
+
+ if ( euler instanceof THREE.Euler === false ) {
+
+ console.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ var te = this.elements;
+
+ var x = euler.x, y = euler.y, z = euler.z;
+ var a = Math.cos( x ), b = Math.sin( x );
+ var c = Math.cos( y ), d = Math.sin( y );
+ var e = Math.cos( z ), f = Math.sin( z );
+
+ if ( euler.order === 'XYZ' ) {
+
+ var ae = a * e, af = a * f, be = b * e, bf = b * f;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = - c * f;
+ te[ 8 ] = d;
+
+ te[ 1 ] = af + be * d;
+ te[ 5 ] = ae - bf * d;
+ te[ 9 ] = - b * c;
+
+ te[ 2 ] = bf - ae * d;
+ te[ 6 ] = be + af * d;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'YXZ' ) {
+
+ var ce = c * e, cf = c * f, de = d * e, df = d * f;
+
+ te[ 0 ] = ce + df * b;
+ te[ 4 ] = de * b - cf;
+ te[ 8 ] = a * d;
+
+ te[ 1 ] = a * f;
+ te[ 5 ] = a * e;
+ te[ 9 ] = - b;
+
+ te[ 2 ] = cf * b - de;
+ te[ 6 ] = df + ce * b;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'ZXY' ) {
+
+ var ce = c * e, cf = c * f, de = d * e, df = d * f;
+
+ te[ 0 ] = ce - df * b;
+ te[ 4 ] = - a * f;
+ te[ 8 ] = de + cf * b;
+
+ te[ 1 ] = cf + de * b;
+ te[ 5 ] = a * e;
+ te[ 9 ] = df - ce * b;
+
+ te[ 2 ] = - a * d;
+ te[ 6 ] = b;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'ZYX' ) {
+
+ var ae = a * e, af = a * f, be = b * e, bf = b * f;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = be * d - af;
+ te[ 8 ] = ae * d + bf;
+
+ te[ 1 ] = c * f;
+ te[ 5 ] = bf * d + ae;
+ te[ 9 ] = af * d - be;
+
+ te[ 2 ] = - d;
+ te[ 6 ] = b * c;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'YZX' ) {
+
+ var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = bd - ac * f;
+ te[ 8 ] = bc * f + ad;
+
+ te[ 1 ] = f;
+ te[ 5 ] = a * e;
+ te[ 9 ] = - b * e;
+
+ te[ 2 ] = - d * e;
+ te[ 6 ] = ad * f + bc;
+ te[ 10 ] = ac - bd * f;
+
+ } else if ( euler.order === 'XZY' ) {
+
+ var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = - f;
+ te[ 8 ] = d * e;
+
+ te[ 1 ] = ac * f + bd;
+ te[ 5 ] = a * e;
+ te[ 9 ] = ad * f - bc;
+
+ te[ 2 ] = bc * f - ad;
+ te[ 6 ] = b * e;
+ te[ 10 ] = bd * f + ac;
+
+ }
+
+ // last column
+ te[ 3 ] = 0;
+ te[ 7 ] = 0;
+ te[ 11 ] = 0;
+
+ // bottom row
+ te[ 12 ] = 0;
+ te[ 13 ] = 0;
+ te[ 14 ] = 0;
+ te[ 15 ] = 1;
+
+ return this;
+
+ },
+
+ setRotationFromQuaternion: function ( q ) {
+
+ console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' );
+
+ return this.makeRotationFromQuaternion( q );
+
+ },
+
+ makeRotationFromQuaternion: function ( q ) {
+
+ var te = this.elements;
+
+ var x = q.x, y = q.y, z = q.z, w = q.w;
+ var x2 = x + x, y2 = y + y, z2 = z + z;
+ var xx = x * x2, xy = x * y2, xz = x * z2;
+ var yy = y * y2, yz = y * z2, zz = z * z2;
+ var wx = w * x2, wy = w * y2, wz = w * z2;
+
+ te[ 0 ] = 1 - ( yy + zz );
+ te[ 4 ] = xy - wz;
+ te[ 8 ] = xz + wy;
+
+ te[ 1 ] = xy + wz;
+ te[ 5 ] = 1 - ( xx + zz );
+ te[ 9 ] = yz - wx;
+
+ te[ 2 ] = xz - wy;
+ te[ 6 ] = yz + wx;
+ te[ 10 ] = 1 - ( xx + yy );
+
+ // last column
+ te[ 3 ] = 0;
+ te[ 7 ] = 0;
+ te[ 11 ] = 0;
+
+ // bottom row
+ te[ 12 ] = 0;
+ te[ 13 ] = 0;
+ te[ 14 ] = 0;
+ te[ 15 ] = 1;
+
+ return this;
+
+ },
+
+ lookAt: function () {
+
+ var x, y, z;
+
+ return function ( eye, target, up ) {
+
+ if ( x === undefined ) x = new THREE.Vector3();
+ if ( y === undefined ) y = new THREE.Vector3();
+ if ( z === undefined ) z = new THREE.Vector3();
+
+ var te = this.elements;
+
+ z.subVectors( eye, target ).normalize();
+
+ if ( z.lengthSq() === 0 ) {
+
+ z.z = 1;
+
+ }
+
+ x.crossVectors( up, z ).normalize();
+
+ if ( x.lengthSq() === 0 ) {
+
+ z.x += 0.0001;
+ x.crossVectors( up, z ).normalize();
+
+ }
+
+ y.crossVectors( z, x );
+
+
+ te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;
+ te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;
+ te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;
+
+ return this;
+
+ };
+
+ }(),
+
+ multiply: function ( m, n ) {
+
+ if ( n !== undefined ) {
+
+ console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
+ return this.multiplyMatrices( m, n );
+
+ }
+
+ return this.multiplyMatrices( this, m );
+
+ },
+
+ multiplyMatrices: function ( a, b ) {
+
+ var ae = a.elements;
+ var be = b.elements;
+ var te = this.elements;
+
+ var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
+ var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
+ var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
+ var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];
+
+ var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
+ var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
+ var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
+ var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];
+
+ te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
+ te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
+ te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
+ te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
+
+ te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
+ te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
+ te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
+ te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
+
+ te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
+ te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
+ te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
+ te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
+
+ te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
+ te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
+ te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
+ te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
+
+ return this;
+
+ },
+
+ multiplyToArray: function ( a, b, r ) {
+
+ var te = this.elements;
+
+ this.multiplyMatrices( a, b );
+
+ r[ 0 ] = te[ 0 ]; r[ 1 ] = te[ 1 ]; r[ 2 ] = te[ 2 ]; r[ 3 ] = te[ 3 ];
+ r[ 4 ] = te[ 4 ]; r[ 5 ] = te[ 5 ]; r[ 6 ] = te[ 6 ]; r[ 7 ] = te[ 7 ];
+ r[ 8 ] = te[ 8 ]; r[ 9 ] = te[ 9 ]; r[ 10 ] = te[ 10 ]; r[ 11 ] = te[ 11 ];
+ r[ 12 ] = te[ 12 ]; r[ 13 ] = te[ 13 ]; r[ 14 ] = te[ 14 ]; r[ 15 ] = te[ 15 ];
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( s ) {
+
+ var te = this.elements;
+
+ te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
+ te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
+ te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
+ te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;
+
+ return this;
+
+ },
+
+ multiplyVector3: function ( vector ) {
+
+ console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' );
+ return vector.applyProjection( this );
+
+ },
+
+ multiplyVector4: function ( vector ) {
+
+ console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
+ return vector.applyMatrix4( this );
+
+ },
+
+ multiplyVector3Array: function ( a ) {
+
+ console.warn( 'THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' );
+ return this.applyToVector3Array( a );
+
+ },
+
+ applyToVector3Array: function () {
+
+ var v1;
+
+ return function ( array, offset, length ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+ if ( offset === undefined ) offset = 0;
+ if ( length === undefined ) length = array.length;
+
+ for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) {
+
+ v1.fromArray( array, j );
+ v1.applyMatrix4( this );
+ v1.toArray( array, j );
+
+ }
+
+ return array;
+
+ };
+
+ }(),
+
+ applyToBuffer: function () {
+
+ var v1;
+
+ return function applyToBuffer( buffer, offset, length ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+ if ( offset === undefined ) offset = 0;
+ if ( length === undefined ) length = buffer.length / buffer.itemSize;
+
+ for ( var i = 0, j = offset; i < length; i ++, j ++ ) {
+
+ v1.x = buffer.getX( j );
+ v1.y = buffer.getY( j );
+ v1.z = buffer.getZ( j );
+
+ v1.applyMatrix4( this );
+
+ buffer.setXYZ( v1.x, v1.y, v1.z );
+
+ }
+
+ return buffer;
+
+ };
+
+ }(),
+
+ rotateAxis: function ( v ) {
+
+ console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' );
+
+ v.transformDirection( this );
+
+ },
+
+ crossVector: function ( vector ) {
+
+ console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
+ return vector.applyMatrix4( this );
+
+ },
+
+ determinant: function () {
+
+ var te = this.elements;
+
+ var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
+ var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
+ var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
+ var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];
+
+ //TODO: make this more efficient
+ //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )
+
+ return (
+ n41 * (
+ + n14 * n23 * n32
+ - n13 * n24 * n32
+ - n14 * n22 * n33
+ + n12 * n24 * n33
+ + n13 * n22 * n34
+ - n12 * n23 * n34
+ ) +
+ n42 * (
+ + n11 * n23 * n34
+ - n11 * n24 * n33
+ + n14 * n21 * n33
+ - n13 * n21 * n34
+ + n13 * n24 * n31
+ - n14 * n23 * n31
+ ) +
+ n43 * (
+ + n11 * n24 * n32
+ - n11 * n22 * n34
+ - n14 * n21 * n32
+ + n12 * n21 * n34
+ + n14 * n22 * n31
+ - n12 * n24 * n31
+ ) +
+ n44 * (
+ - n13 * n22 * n31
+ - n11 * n23 * n32
+ + n11 * n22 * n33
+ + n13 * n21 * n32
+ - n12 * n21 * n33
+ + n12 * n23 * n31
+ )
+
+ );
+
+ },
+
+ transpose: function () {
+
+ var te = this.elements;
+ var tmp;
+
+ tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
+ tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
+ tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;
+
+ tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
+ tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
+ tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;
+
+ return this;
+
+ },
+
+ flattenToArrayOffset: function ( array, offset ) {
+
+ var te = this.elements;
+
+ array[ offset ] = te[ 0 ];
+ array[ offset + 1 ] = te[ 1 ];
+ array[ offset + 2 ] = te[ 2 ];
+ array[ offset + 3 ] = te[ 3 ];
+
+ array[ offset + 4 ] = te[ 4 ];
+ array[ offset + 5 ] = te[ 5 ];
+ array[ offset + 6 ] = te[ 6 ];
+ array[ offset + 7 ] = te[ 7 ];
+
+ array[ offset + 8 ] = te[ 8 ];
+ array[ offset + 9 ] = te[ 9 ];
+ array[ offset + 10 ] = te[ 10 ];
+ array[ offset + 11 ] = te[ 11 ];
+
+ array[ offset + 12 ] = te[ 12 ];
+ array[ offset + 13 ] = te[ 13 ];
+ array[ offset + 14 ] = te[ 14 ];
+ array[ offset + 15 ] = te[ 15 ];
+
+ return array;
+
+ },
+
+ getPosition: function () {
+
+ var v1;
+
+ return function () {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+ console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' );
+
+ var te = this.elements;
+ return v1.set( te[ 12 ], te[ 13 ], te[ 14 ] );
+
+ };
+
+ }(),
+
+ setPosition: function ( v ) {
+
+ var te = this.elements;
+
+ te[ 12 ] = v.x;
+ te[ 13 ] = v.y;
+ te[ 14 ] = v.z;
+
+ return this;
+
+ },
+
+ getInverse: function ( m, throwOnInvertible ) {
+
+ // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
+ var te = this.elements;
+ var me = m.elements;
+
+ var n11 = me[ 0 ], n12 = me[ 4 ], n13 = me[ 8 ], n14 = me[ 12 ];
+ var n21 = me[ 1 ], n22 = me[ 5 ], n23 = me[ 9 ], n24 = me[ 13 ];
+ var n31 = me[ 2 ], n32 = me[ 6 ], n33 = me[ 10 ], n34 = me[ 14 ];
+ var n41 = me[ 3 ], n42 = me[ 7 ], n43 = me[ 11 ], n44 = me[ 15 ];
+
+ te[ 0 ] = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44;
+ te[ 4 ] = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44;
+ te[ 8 ] = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44;
+ te[ 12 ] = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
+ te[ 1 ] = n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44;
+ te[ 5 ] = n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44;
+ te[ 9 ] = n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44;
+ te[ 13 ] = n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34;
+ te[ 2 ] = n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44;
+ te[ 6 ] = n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44;
+ te[ 10 ] = n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44;
+ te[ 14 ] = n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34;
+ te[ 3 ] = n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43;
+ te[ 7 ] = n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43;
+ te[ 11 ] = n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43;
+ te[ 15 ] = n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33;
+
+ var det = n11 * te[ 0 ] + n21 * te[ 4 ] + n31 * te[ 8 ] + n41 * te[ 12 ];
+
+ if ( det === 0 ) {
+
+ var msg = "THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0";
+
+ if ( throwOnInvertible || false ) {
+
+ throw new Error( msg );
+
+ } else {
+
+ console.warn( msg );
+
+ }
+
+ this.identity();
+
+ return this;
+
+ }
+
+ this.multiplyScalar( 1 / det );
+
+ return this;
+
+ },
+
+ translate: function ( v ) {
+
+ console.error( 'THREE.Matrix4: .translate() has been removed.' );
+
+ },
+
+ rotateX: function ( angle ) {
+
+ console.error( 'THREE.Matrix4: .rotateX() has been removed.' );
+
+ },
+
+ rotateY: function ( angle ) {
+
+ console.error( 'THREE.Matrix4: .rotateY() has been removed.' );
+
+ },
+
+ rotateZ: function ( angle ) {
+
+ console.error( 'THREE.Matrix4: .rotateZ() has been removed.' );
+
+ },
+
+ rotateByAxis: function ( axis, angle ) {
+
+ console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' );
+
+ },
+
+ scale: function ( v ) {
+
+ var te = this.elements;
+ var x = v.x, y = v.y, z = v.z;
+
+ te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;
+ te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;
+ te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;
+ te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;
+
+ return this;
+
+ },
+
+ getMaxScaleOnAxis: function () {
+
+ var te = this.elements;
+
+ var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];
+ var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];
+ var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];
+
+ return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );
+
+ },
+
+ makeTranslation: function ( x, y, z ) {
+
+ this.set(
+
+ 1, 0, 0, x,
+ 0, 1, 0, y,
+ 0, 0, 1, z,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationX: function ( theta ) {
+
+ var c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ 1, 0, 0, 0,
+ 0, c, - s, 0,
+ 0, s, c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationY: function ( theta ) {
+
+ var c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ c, 0, s, 0,
+ 0, 1, 0, 0,
+ - s, 0, c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationZ: function ( theta ) {
+
+ var c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ c, - s, 0, 0,
+ s, c, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationAxis: function ( axis, angle ) {
+
+ // Based on http://www.gamedev.net/reference/articles/article1199.asp
+
+ var c = Math.cos( angle );
+ var s = Math.sin( angle );
+ var t = 1 - c;
+ var x = axis.x, y = axis.y, z = axis.z;
+ var tx = t * x, ty = t * y;
+
+ this.set(
+
+ tx * x + c, tx * y - s * z, tx * z + s * y, 0,
+ tx * y + s * z, ty * y + c, ty * z - s * x, 0,
+ tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeScale: function ( x, y, z ) {
+
+ this.set(
+
+ x, 0, 0, 0,
+ 0, y, 0, 0,
+ 0, 0, z, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ compose: function ( position, quaternion, scale ) {
+
+ this.makeRotationFromQuaternion( quaternion );
+ this.scale( scale );
+ this.setPosition( position );
+
+ return this;
+
+ },
+
+ decompose: function () {
+
+ var vector, matrix;
+
+ return function ( position, quaternion, scale ) {
+
+ if ( vector === undefined ) vector = new THREE.Vector3();
+ if ( matrix === undefined ) matrix = new THREE.Matrix4();
+
+ var te = this.elements;
+
+ var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
+ var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
+ var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();
+
+ // if determine is negative, we need to invert one scale
+ var det = this.determinant();
+ if ( det < 0 ) {
+
+ sx = - sx;
+
+ }
+
+ position.x = te[ 12 ];
+ position.y = te[ 13 ];
+ position.z = te[ 14 ];
+
+ // scale the rotation part
+
+ matrix.elements.set( this.elements ); // at this point matrix is incomplete so we can't use .copy()
+
+ var invSX = 1 / sx;
+ var invSY = 1 / sy;
+ var invSZ = 1 / sz;
+
+ matrix.elements[ 0 ] *= invSX;
+ matrix.elements[ 1 ] *= invSX;
+ matrix.elements[ 2 ] *= invSX;
+
+ matrix.elements[ 4 ] *= invSY;
+ matrix.elements[ 5 ] *= invSY;
+ matrix.elements[ 6 ] *= invSY;
+
+ matrix.elements[ 8 ] *= invSZ;
+ matrix.elements[ 9 ] *= invSZ;
+ matrix.elements[ 10 ] *= invSZ;
+
+ quaternion.setFromRotationMatrix( matrix );
+
+ scale.x = sx;
+ scale.y = sy;
+ scale.z = sz;
+
+ return this;
+
+ };
+
+ }(),
+
+ makeFrustum: function ( left, right, bottom, top, near, far ) {
+
+ var te = this.elements;
+ var x = 2 * near / ( right - left );
+ var y = 2 * near / ( top - bottom );
+
+ var a = ( right + left ) / ( right - left );
+ var b = ( top + bottom ) / ( top - bottom );
+ var c = - ( far + near ) / ( far - near );
+ var d = - 2 * far * near / ( far - near );
+
+ te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0;
+ te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0;
+ te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d;
+ te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0;
+
+ return this;
+
+ },
+
+ makePerspective: function ( fov, aspect, near, far ) {
+
+ var ymax = near * Math.tan( THREE.Math.degToRad( fov * 0.5 ) );
+ var ymin = - ymax;
+ var xmin = ymin * aspect;
+ var xmax = ymax * aspect;
+
+ return this.makeFrustum( xmin, xmax, ymin, ymax, near, far );
+
+ },
+
+ makeOrthographic: function ( left, right, top, bottom, near, far ) {
+
+ var te = this.elements;
+ var w = right - left;
+ var h = top - bottom;
+ var p = far - near;
+
+ var x = ( right + left ) / w;
+ var y = ( top + bottom ) / h;
+ var z = ( far + near ) / p;
+
+ te[ 0 ] = 2 / w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x;
+ te[ 1 ] = 0; te[ 5 ] = 2 / h; te[ 9 ] = 0; te[ 13 ] = - y;
+ te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 / p; te[ 14 ] = - z;
+ te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1;
+
+ return this;
+
+ },
+
+ equals: function ( matrix ) {
+
+ var te = this.elements;
+ var me = matrix.elements;
+
+ for ( var i = 0; i < 16; i ++ ) {
+
+ if ( te[ i ] !== me[ i ] ) return false;
+
+ }
+
+ return true;
+
+ },
+
+ fromArray: function ( array ) {
+
+ this.elements.set( array );
+
+ return this;
+
+ },
+
+ toArray: function () {
+
+ var te = this.elements;
+
+ return [
+ te[ 0 ], te[ 1 ], te[ 2 ], te[ 3 ],
+ te[ 4 ], te[ 5 ], te[ 6 ], te[ 7 ],
+ te[ 8 ], te[ 9 ], te[ 10 ], te[ 11 ],
+ te[ 12 ], te[ 13 ], te[ 14 ], te[ 15 ]
+ ];
+
+ }
+
+};
+
+// File:src/math/Ray.js
+
+/**
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Ray = function ( origin, direction ) {
+
+ this.origin = ( origin !== undefined ) ? origin : new THREE.Vector3();
+ this.direction = ( direction !== undefined ) ? direction : new THREE.Vector3();
+
+};
+
+THREE.Ray.prototype = {
+
+ constructor: THREE.Ray,
+
+ set: function ( origin, direction ) {
+
+ this.origin.copy( origin );
+ this.direction.copy( direction );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( ray ) {
+
+ this.origin.copy( ray.origin );
+ this.direction.copy( ray.direction );
+
+ return this;
+
+ },
+
+ at: function ( t, optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ return result.copy( this.direction ).multiplyScalar( t ).add( this.origin );
+
+ },
+
+ recast: function () {
+
+ var v1 = new THREE.Vector3();
+
+ return function ( t ) {
+
+ this.origin.copy( this.at( t, v1 ) );
+
+ return this;
+
+ };
+
+ }(),
+
+ closestPointToPoint: function ( point, optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ result.subVectors( point, this.origin );
+ var directionDistance = result.dot( this.direction );
+
+ if ( directionDistance < 0 ) {
+
+ return result.copy( this.origin );
+
+ }
+
+ return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
+
+ },
+
+ distanceToPoint: function ( point ) {
+
+ return Math.sqrt( this.distanceSqToPoint( point ) );
+
+ },
+
+ distanceSqToPoint: function () {
+
+ var v1 = new THREE.Vector3();
+
+ return function ( point ) {
+
+ var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction );
+
+ // point behind the ray
+
+ if ( directionDistance < 0 ) {
+
+ return this.origin.distanceToSquared( point );
+
+ }
+
+ v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
+
+ return v1.distanceToSquared( point );
+
+ };
+
+ }(),
+
+ distanceSqToSegment: function () {
+
+ var segCenter = new THREE.Vector3();
+ var segDir = new THREE.Vector3();
+ var diff = new THREE.Vector3();
+
+ return function ( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {
+
+ // from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp
+ // It returns the min distance between the ray and the segment
+ // defined by v0 and v1
+ // It can also set two optional targets :
+ // - The closest point on the ray
+ // - The closest point on the segment
+
+ segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );
+ segDir.copy( v1 ).sub( v0 ).normalize();
+ diff.copy( this.origin ).sub( segCenter );
+
+ var segExtent = v0.distanceTo( v1 ) * 0.5;
+ var a01 = - this.direction.dot( segDir );
+ var b0 = diff.dot( this.direction );
+ var b1 = - diff.dot( segDir );
+ var c = diff.lengthSq();
+ var det = Math.abs( 1 - a01 * a01 );
+ var s0, s1, sqrDist, extDet;
+
+ if ( det > 0 ) {
+
+ // The ray and segment are not parallel.
+
+ s0 = a01 * b1 - b0;
+ s1 = a01 * b0 - b1;
+ extDet = segExtent * det;
+
+ if ( s0 >= 0 ) {
+
+ if ( s1 >= - extDet ) {
+
+ if ( s1 <= extDet ) {
+
+ // region 0
+ // Minimum at interior points of ray and segment.
+
+ var invDet = 1 / det;
+ s0 *= invDet;
+ s1 *= invDet;
+ sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;
+
+ } else {
+
+ // region 1
+
+ s1 = segExtent;
+ s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ }
+
+ } else {
+
+ // region 5
+
+ s1 = - segExtent;
+ s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ }
+
+ } else {
+
+ if ( s1 <= - extDet ) {
+
+ // region 4
+
+ s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );
+ s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ } else if ( s1 <= extDet ) {
+
+ // region 3
+
+ s0 = 0;
+ s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );
+ sqrDist = s1 * ( s1 + 2 * b1 ) + c;
+
+ } else {
+
+ // region 2
+
+ s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );
+ s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ }
+
+ }
+
+ } else {
+
+ // Ray and segment are parallel.
+
+ s1 = ( a01 > 0 ) ? - segExtent : segExtent;
+ s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ }
+
+ if ( optionalPointOnRay ) {
+
+ optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin );
+
+ }
+
+ if ( optionalPointOnSegment ) {
+
+ optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter );
+
+ }
+
+ return sqrDist;
+
+ };
+
+ }(),
+
+
+ isIntersectionSphere: function ( sphere ) {
+
+ return this.distanceToPoint( sphere.center ) <= sphere.radius;
+
+ },
+
+ intersectSphere: function () {
+
+ // from http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-sphere-intersection/
+
+ var v1 = new THREE.Vector3();
+
+ return function ( sphere, optionalTarget ) {
+
+ v1.subVectors( sphere.center, this.origin );
+
+ var tca = v1.dot( this.direction );
+
+ var d2 = v1.dot( v1 ) - tca * tca;
+
+ var radius2 = sphere.radius * sphere.radius;
+
+ if ( d2 > radius2 ) return null;
+
+ var thc = Math.sqrt( radius2 - d2 );
+
+ // t0 = first intersect point - entrance on front of sphere
+ var t0 = tca - thc;
+
+ // t1 = second intersect point - exit point on back of sphere
+ var t1 = tca + thc;
+
+ // test to see if both t0 and t1 are behind the ray - if so, return null
+ if ( t0 < 0 && t1 < 0 ) return null;
+
+ // test to see if t0 is behind the ray:
+ // if it is, the ray is inside the sphere, so return the second exit point scaled by t1,
+ // in order to always return an intersect point that is in front of the ray.
+ if ( t0 < 0 ) return this.at( t1, optionalTarget );
+
+ // else t0 is in front of the ray, so return the first collision point scaled by t0
+ return this.at( t0, optionalTarget );
+
+ }
+
+ }(),
+
+ isIntersectionPlane: function ( plane ) {
+
+ // check if the ray lies on the plane first
+
+ var distToPoint = plane.distanceToPoint( this.origin );
+
+ if ( distToPoint === 0 ) {
+
+ return true;
+
+ }
+
+ var denominator = plane.normal.dot( this.direction );
+
+ if ( denominator * distToPoint < 0 ) {
+
+ return true;
+
+ }
+
+ // ray origin is behind the plane (and is pointing behind it)
+
+ return false;
+
+ },
+
+ distanceToPlane: function ( plane ) {
+
+ var denominator = plane.normal.dot( this.direction );
+ if ( denominator === 0 ) {
+
+ // line is coplanar, return origin
+ if ( plane.distanceToPoint( this.origin ) === 0 ) {
+
+ return 0;
+
+ }
+
+ // Null is preferable to undefined since undefined means.... it is undefined
+
+ return null;
+
+ }
+
+ var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;
+
+ // Return if the ray never intersects the plane
+
+ return t >= 0 ? t : null;
+
+ },
+
+ intersectPlane: function ( plane, optionalTarget ) {
+
+ var t = this.distanceToPlane( plane );
+
+ if ( t === null ) {
+
+ return null;
+
+ }
+
+ return this.at( t, optionalTarget );
+
+ },
+
+ isIntersectionBox: function () {
+
+ var v = new THREE.Vector3();
+
+ return function ( box ) {
+
+ return this.intersectBox( box, v ) !== null;
+
+ };
+
+ }(),
+
+ intersectBox: function ( box, optionalTarget ) {
+
+ // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/
+
+ var tmin, tmax, tymin, tymax, tzmin, tzmax;
+
+ var invdirx = 1 / this.direction.x,
+ invdiry = 1 / this.direction.y,
+ invdirz = 1 / this.direction.z;
+
+ var origin = this.origin;
+
+ if ( invdirx >= 0 ) {
+
+ tmin = ( box.min.x - origin.x ) * invdirx;
+ tmax = ( box.max.x - origin.x ) * invdirx;
+
+ } else {
+
+ tmin = ( box.max.x - origin.x ) * invdirx;
+ tmax = ( box.min.x - origin.x ) * invdirx;
+
+ }
+
+ if ( invdiry >= 0 ) {
+
+ tymin = ( box.min.y - origin.y ) * invdiry;
+ tymax = ( box.max.y - origin.y ) * invdiry;
+
+ } else {
+
+ tymin = ( box.max.y - origin.y ) * invdiry;
+ tymax = ( box.min.y - origin.y ) * invdiry;
+
+ }
+
+ if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;
+
+ // These lines also handle the case where tmin or tmax is NaN
+ // (result of 0 * Infinity). x !== x returns true if x is NaN
+
+ if ( tymin > tmin || tmin !== tmin ) tmin = tymin;
+
+ if ( tymax < tmax || tmax !== tmax ) tmax = tymax;
+
+ if ( invdirz >= 0 ) {
+
+ tzmin = ( box.min.z - origin.z ) * invdirz;
+ tzmax = ( box.max.z - origin.z ) * invdirz;
+
+ } else {
+
+ tzmin = ( box.max.z - origin.z ) * invdirz;
+ tzmax = ( box.min.z - origin.z ) * invdirz;
+
+ }
+
+ if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;
+
+ if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;
+
+ if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;
+
+ //return point closest to the ray (positive side)
+
+ if ( tmax < 0 ) return null;
+
+ return this.at( tmin >= 0 ? tmin : tmax, optionalTarget );
+
+ },
+
+ intersectTriangle: function () {
+
+ // Compute the offset origin, edges, and normal.
+ var diff = new THREE.Vector3();
+ var edge1 = new THREE.Vector3();
+ var edge2 = new THREE.Vector3();
+ var normal = new THREE.Vector3();
+
+ return function ( a, b, c, backfaceCulling, optionalTarget ) {
+
+ // from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrRay3Triangle3.cpp
+
+ edge1.subVectors( b, a );
+ edge2.subVectors( c, a );
+ normal.crossVectors( edge1, edge2 );
+
+ // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,
+ // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by
+ // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))
+ // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))
+ // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)
+ var DdN = this.direction.dot( normal );
+ var sign;
+
+ if ( DdN > 0 ) {
+
+ if ( backfaceCulling ) return null;
+ sign = 1;
+
+ } else if ( DdN < 0 ) {
+
+ sign = - 1;
+ DdN = - DdN;
+
+ } else {
+
+ return null;
+
+ }
+
+ diff.subVectors( this.origin, a );
+ var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) );
+
+ // b1 < 0, no intersection
+ if ( DdQxE2 < 0 ) {
+
+ return null;
+
+ }
+
+ var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) );
+
+ // b2 < 0, no intersection
+ if ( DdE1xQ < 0 ) {
+
+ return null;
+
+ }
+
+ // b1+b2 > 1, no intersection
+ if ( DdQxE2 + DdE1xQ > DdN ) {
+
+ return null;
+
+ }
+
+ // Line intersects triangle, check if ray does.
+ var QdN = - sign * diff.dot( normal );
+
+ // t < 0, no intersection
+ if ( QdN < 0 ) {
+
+ return null;
+
+ }
+
+ // Ray intersects triangle.
+ return this.at( QdN / DdN, optionalTarget );
+
+ };
+
+ }(),
+
+ applyMatrix4: function ( matrix4 ) {
+
+ this.direction.add( this.origin ).applyMatrix4( matrix4 );
+ this.origin.applyMatrix4( matrix4 );
+ this.direction.sub( this.origin );
+ this.direction.normalize();
+
+ return this;
+
+ },
+
+ equals: function ( ray ) {
+
+ return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );
+
+ }
+
+};
+
+// File:src/math/Sphere.js
+
+/**
+ * @author bhouston / http://clara.io
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Sphere = function ( center, radius ) {
+
+ this.center = ( center !== undefined ) ? center : new THREE.Vector3();
+ this.radius = ( radius !== undefined ) ? radius : 0;
+
+};
+
+THREE.Sphere.prototype = {
+
+ constructor: THREE.Sphere,
+
+ set: function ( center, radius ) {
+
+ this.center.copy( center );
+ this.radius = radius;
+
+ return this;
+
+ },
+
+ setFromPoints: function () {
+
+ var box = new THREE.Box3();
+
+ return function ( points, optionalCenter ) {
+
+ var center = this.center;
+
+ if ( optionalCenter !== undefined ) {
+
+ center.copy( optionalCenter );
+
+ } else {
+
+ box.setFromPoints( points ).center( center );
+
+ }
+
+ var maxRadiusSq = 0;
+
+ for ( var i = 0, il = points.length; i < il; i ++ ) {
+
+ maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) );
+
+ }
+
+ this.radius = Math.sqrt( maxRadiusSq );
+
+ return this;
+
+ };
+
+ }(),
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( sphere ) {
+
+ this.center.copy( sphere.center );
+ this.radius = sphere.radius;
+
+ return this;
+
+ },
+
+ empty: function () {
+
+ return ( this.radius <= 0 );
+
+ },
+
+ containsPoint: function ( point ) {
+
+ return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) );
+
+ },
+
+ distanceToPoint: function ( point ) {
+
+ return ( point.distanceTo( this.center ) - this.radius );
+
+ },
+
+ intersectsSphere: function ( sphere ) {
+
+ var radiusSum = this.radius + sphere.radius;
+
+ return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum );
+
+ },
+
+ clampPoint: function ( point, optionalTarget ) {
+
+ var deltaLengthSq = this.center.distanceToSquared( point );
+
+ var result = optionalTarget || new THREE.Vector3();
+ result.copy( point );
+
+ if ( deltaLengthSq > ( this.radius * this.radius ) ) {
+
+ result.sub( this.center ).normalize();
+ result.multiplyScalar( this.radius ).add( this.center );
+
+ }
+
+ return result;
+
+ },
+
+ getBoundingBox: function ( optionalTarget ) {
+
+ var box = optionalTarget || new THREE.Box3();
+
+ box.set( this.center, this.center );
+ box.expandByScalar( this.radius );
+
+ return box;
+
+ },
+
+ applyMatrix4: function ( matrix ) {
+
+ this.center.applyMatrix4( matrix );
+ this.radius = this.radius * matrix.getMaxScaleOnAxis();
+
+ return this;
+
+ },
+
+ translate: function ( offset ) {
+
+ this.center.add( offset );
+
+ return this;
+
+ },
+
+ equals: function ( sphere ) {
+
+ return sphere.center.equals( this.center ) && ( sphere.radius === this.radius );
+
+ }
+
+};
+
+// File:src/math/Frustum.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Frustum = function ( p0, p1, p2, p3, p4, p5 ) {
+
+ this.planes = [
+
+ ( p0 !== undefined ) ? p0 : new THREE.Plane(),
+ ( p1 !== undefined ) ? p1 : new THREE.Plane(),
+ ( p2 !== undefined ) ? p2 : new THREE.Plane(),
+ ( p3 !== undefined ) ? p3 : new THREE.Plane(),
+ ( p4 !== undefined ) ? p4 : new THREE.Plane(),
+ ( p5 !== undefined ) ? p5 : new THREE.Plane()
+
+ ];
+
+};
+
+THREE.Frustum.prototype = {
+
+ constructor: THREE.Frustum,
+
+ set: function ( p0, p1, p2, p3, p4, p5 ) {
+
+ var planes = this.planes;
+
+ planes[ 0 ].copy( p0 );
+ planes[ 1 ].copy( p1 );
+ planes[ 2 ].copy( p2 );
+ planes[ 3 ].copy( p3 );
+ planes[ 4 ].copy( p4 );
+ planes[ 5 ].copy( p5 );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( frustum ) {
+
+ var planes = this.planes;
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ planes[ i ].copy( frustum.planes[ i ] );
+
+ }
+
+ return this;
+
+ },
+
+ setFromMatrix: function ( m ) {
+
+ var planes = this.planes;
+ var me = m.elements;
+ var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];
+ var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];
+ var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];
+ var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];
+
+ planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();
+ planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();
+ planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();
+ planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();
+ planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();
+ planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize();
+
+ return this;
+
+ },
+
+ intersectsObject: function () {
+
+ var sphere = new THREE.Sphere();
+
+ return function ( object ) {
+
+ var geometry = object.geometry;
+
+ if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
+
+ sphere.copy( geometry.boundingSphere );
+ sphere.applyMatrix4( object.matrixWorld );
+
+ return this.intersectsSphere( sphere );
+
+ };
+
+ }(),
+
+ intersectsSphere: function ( sphere ) {
+
+ var planes = this.planes;
+ var center = sphere.center;
+ var negRadius = - sphere.radius;
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ var distance = planes[ i ].distanceToPoint( center );
+
+ if ( distance < negRadius ) {
+
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+ },
+
+ intersectsBox: function () {
+
+ var p1 = new THREE.Vector3(),
+ p2 = new THREE.Vector3();
+
+ return function ( box ) {
+
+ var planes = this.planes;
+
+ for ( var i = 0; i < 6 ; i ++ ) {
+
+ var plane = planes[ i ];
+
+ p1.x = plane.normal.x > 0 ? box.min.x : box.max.x;
+ p2.x = plane.normal.x > 0 ? box.max.x : box.min.x;
+ p1.y = plane.normal.y > 0 ? box.min.y : box.max.y;
+ p2.y = plane.normal.y > 0 ? box.max.y : box.min.y;
+ p1.z = plane.normal.z > 0 ? box.min.z : box.max.z;
+ p2.z = plane.normal.z > 0 ? box.max.z : box.min.z;
+
+ var d1 = plane.distanceToPoint( p1 );
+ var d2 = plane.distanceToPoint( p2 );
+
+ // if both outside plane, no intersection
+
+ if ( d1 < 0 && d2 < 0 ) {
+
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+ };
+
+ }(),
+
+
+ containsPoint: function ( point ) {
+
+ var planes = this.planes;
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ if ( planes[ i ].distanceToPoint( point ) < 0 ) {
+
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+ }
+
+};
+
+// File:src/math/Plane.js
+
+/**
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Plane = function ( normal, constant ) {
+
+ this.normal = ( normal !== undefined ) ? normal : new THREE.Vector3( 1, 0, 0 );
+ this.constant = ( constant !== undefined ) ? constant : 0;
+
+};
+
+THREE.Plane.prototype = {
+
+ constructor: THREE.Plane,
+
+ set: function ( normal, constant ) {
+
+ this.normal.copy( normal );
+ this.constant = constant;
+
+ return this;
+
+ },
+
+ setComponents: function ( x, y, z, w ) {
+
+ this.normal.set( x, y, z );
+ this.constant = w;
+
+ return this;
+
+ },
+
+ setFromNormalAndCoplanarPoint: function ( normal, point ) {
+
+ this.normal.copy( normal );
+ this.constant = - point.dot( this.normal ); // must be this.normal, not normal, as this.normal is normalized
+
+ return this;
+
+ },
+
+ setFromCoplanarPoints: function () {
+
+ var v1 = new THREE.Vector3();
+ var v2 = new THREE.Vector3();
+
+ return function ( a, b, c ) {
+
+ var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize();
+
+ // Q: should an error be thrown if normal is zero (e.g. degenerate plane)?
+
+ this.setFromNormalAndCoplanarPoint( normal, a );
+
+ return this;
+
+ };
+
+ }(),
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( plane ) {
+
+ this.normal.copy( plane.normal );
+ this.constant = plane.constant;
+
+ return this;
+
+ },
+
+ normalize: function () {
+
+ // Note: will lead to a divide by zero if the plane is invalid.
+
+ var inverseNormalLength = 1.0 / this.normal.length();
+ this.normal.multiplyScalar( inverseNormalLength );
+ this.constant *= inverseNormalLength;
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.constant *= - 1;
+ this.normal.negate();
+
+ return this;
+
+ },
+
+ distanceToPoint: function ( point ) {
+
+ return this.normal.dot( point ) + this.constant;
+
+ },
+
+ distanceToSphere: function ( sphere ) {
+
+ return this.distanceToPoint( sphere.center ) - sphere.radius;
+
+ },
+
+ projectPoint: function ( point, optionalTarget ) {
+
+ return this.orthoPoint( point, optionalTarget ).sub( point ).negate();
+
+ },
+
+ orthoPoint: function ( point, optionalTarget ) {
+
+ var perpendicularMagnitude = this.distanceToPoint( point );
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.copy( this.normal ).multiplyScalar( perpendicularMagnitude );
+
+ },
+
+ isIntersectionLine: function ( line ) {
+
+ // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.
+
+ var startSign = this.distanceToPoint( line.start );
+ var endSign = this.distanceToPoint( line.end );
+
+ return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );
+
+ },
+
+ intersectLine: function () {
+
+ var v1 = new THREE.Vector3();
+
+ return function ( line, optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ var direction = line.delta( v1 );
+
+ var denominator = this.normal.dot( direction );
+
+ if ( denominator === 0 ) {
+
+ // line is coplanar, return origin
+ if ( this.distanceToPoint( line.start ) === 0 ) {
+
+ return result.copy( line.start );
+
+ }
+
+ // Unsure if this is the correct method to handle this case.
+ return undefined;
+
+ }
+
+ var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;
+
+ if ( t < 0 || t > 1 ) {
+
+ return undefined;
+
+ }
+
+ return result.copy( direction ).multiplyScalar( t ).add( line.start );
+
+ };
+
+ }(),
+
+
+ coplanarPoint: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.copy( this.normal ).multiplyScalar( - this.constant );
+
+ },
+
+ applyMatrix4: function () {
+
+ var v1 = new THREE.Vector3();
+ var v2 = new THREE.Vector3();
+ var m1 = new THREE.Matrix3();
+
+ return function ( matrix, optionalNormalMatrix ) {
+
+ // compute new normal based on theory here:
+ // http://www.songho.ca/opengl/gl_normaltransform.html
+ var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix );
+ var newNormal = v1.copy( this.normal ).applyMatrix3( normalMatrix );
+
+ var newCoplanarPoint = this.coplanarPoint( v2 );
+ newCoplanarPoint.applyMatrix4( matrix );
+
+ this.setFromNormalAndCoplanarPoint( newNormal, newCoplanarPoint );
+
+ return this;
+
+ };
+
+ }(),
+
+ translate: function ( offset ) {
+
+ this.constant = this.constant - offset.dot( this.normal );
+
+ return this;
+
+ },
+
+ equals: function ( plane ) {
+
+ return plane.normal.equals( this.normal ) && ( plane.constant === this.constant );
+
+ }
+
+};
+
+// File:src/math/Math.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Math = {
+
+ generateUUID: function () {
+
+ // http://www.broofa.com/Tools/Math.uuid.htm
+
+ var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' );
+ var uuid = new Array( 36 );
+ var rnd = 0, r;
+
+ return function () {
+
+ for ( var i = 0; i < 36; i ++ ) {
+
+ if ( i === 8 || i === 13 || i === 18 || i === 23 ) {
+
+ uuid[ i ] = '-';
+
+ } else if ( i === 14 ) {
+
+ uuid[ i ] = '4';
+
+ } else {
+
+ if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
+ r = rnd & 0xf;
+ rnd = rnd >> 4;
+ uuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ];
+
+ }
+
+ }
+
+ return uuid.join( '' );
+
+ };
+
+ }(),
+
+ clamp: function ( value, min, max ) {
+
+ return Math.max( min, Math.min( max, value ) );
+
+ },
+
+ // compute euclidian modulo of m % n
+ // https://en.wikipedia.org/wiki/Modulo_operation
+
+ euclideanModulo: function ( n, m ) {
+
+ return ( ( n % m ) + m ) % m;
+
+ },
+
+ // Linear mapping from range to range
+
+ mapLinear: function ( x, a1, a2, b1, b2 ) {
+
+ return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
+
+ },
+
+ // http://en.wikipedia.org/wiki/Smoothstep
+
+ smoothstep: function ( x, min, max ) {
+
+ if ( x <= min ) return 0;
+ if ( x >= max ) return 1;
+
+ x = ( x - min ) / ( max - min );
+
+ return x * x * ( 3 - 2 * x );
+
+ },
+
+ smootherstep: function ( x, min, max ) {
+
+ if ( x <= min ) return 0;
+ if ( x >= max ) return 1;
+
+ x = ( x - min ) / ( max - min );
+
+ return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
+
+ },
+
+ // Random float from <0, 1> with 16 bits of randomness
+ // (standard Math.random() creates repetitive patterns when applied over larger space)
+
+ random16: function () {
+
+ return ( 65280 * Math.random() + 255 * Math.random() ) / 65535;
+
+ },
+
+ // Random integer from interval
+
+ randInt: function ( low, high ) {
+
+ return low + Math.floor( Math.random() * ( high - low + 1 ) );
+
+ },
+
+ // Random float from interval
+
+ randFloat: function ( low, high ) {
+
+ return low + Math.random() * ( high - low );
+
+ },
+
+ // Random float from <-range/2, range/2> interval
+
+ randFloatSpread: function ( range ) {
+
+ return range * ( 0.5 - Math.random() );
+
+ },
+
+ degToRad: function () {
+
+ var degreeToRadiansFactor = Math.PI / 180;
+
+ return function ( degrees ) {
+
+ return degrees * degreeToRadiansFactor;
+
+ };
+
+ }(),
+
+ radToDeg: function () {
+
+ var radianToDegreesFactor = 180 / Math.PI;
+
+ return function ( radians ) {
+
+ return radians * radianToDegreesFactor;
+
+ };
+
+ }(),
+
+ isPowerOfTwo: function ( value ) {
+
+ return ( value & ( value - 1 ) ) === 0 && value !== 0;
+
+ },
+
+ nearestPowerOfTwo: function ( value ) {
+
+ return Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) );
+
+ },
+
+ nextPowerOfTwo: function ( value ) {
+
+ value --;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ value ++;
+
+ return value;
+
+ }
+
+};
+
+// File:src/math/Spline.js
+
+/**
+ * Spline from Tween.js, slightly optimized (and trashed)
+ * http://sole.github.com/tween.js/examples/05_spline.html
+ *
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Spline = function ( points ) {
+
+ this.points = points;
+
+ var c = [], v3 = { x: 0, y: 0, z: 0 },
+ point, intPoint, weight, w2, w3,
+ pa, pb, pc, pd;
+
+ this.initFromArray = function ( a ) {
+
+ this.points = [];
+
+ for ( var i = 0; i < a.length; i ++ ) {
+
+ this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] };
+
+ }
+
+ };
+
+ this.getPoint = function ( k ) {
+
+ point = ( this.points.length - 1 ) * k;
+ intPoint = Math.floor( point );
+ weight = point - intPoint;
+
+ c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1;
+ c[ 1 ] = intPoint;
+ c[ 2 ] = intPoint > this.points.length - 2 ? this.points.length - 1 : intPoint + 1;
+ c[ 3 ] = intPoint > this.points.length - 3 ? this.points.length - 1 : intPoint + 2;
+
+ pa = this.points[ c[ 0 ] ];
+ pb = this.points[ c[ 1 ] ];
+ pc = this.points[ c[ 2 ] ];
+ pd = this.points[ c[ 3 ] ];
+
+ w2 = weight * weight;
+ w3 = weight * w2;
+
+ v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 );
+ v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 );
+ v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 );
+
+ return v3;
+
+ };
+
+ this.getControlPointsArray = function () {
+
+ var i, p, l = this.points.length,
+ coords = [];
+
+ for ( i = 0; i < l; i ++ ) {
+
+ p = this.points[ i ];
+ coords[ i ] = [ p.x, p.y, p.z ];
+
+ }
+
+ return coords;
+
+ };
+
+ // approximate length by summing linear segments
+
+ this.getLength = function ( nSubDivisions ) {
+
+ var i, index, nSamples, position,
+ point = 0, intPoint = 0, oldIntPoint = 0,
+ oldPosition = new THREE.Vector3(),
+ tmpVec = new THREE.Vector3(),
+ chunkLengths = [],
+ totalLength = 0;
+
+ // first point has 0 length
+
+ chunkLengths[ 0 ] = 0;
+
+ if ( ! nSubDivisions ) nSubDivisions = 100;
+
+ nSamples = this.points.length * nSubDivisions;
+
+ oldPosition.copy( this.points[ 0 ] );
+
+ for ( i = 1; i < nSamples; i ++ ) {
+
+ index = i / nSamples;
+
+ position = this.getPoint( index );
+ tmpVec.copy( position );
+
+ totalLength += tmpVec.distanceTo( oldPosition );
+
+ oldPosition.copy( position );
+
+ point = ( this.points.length - 1 ) * index;
+ intPoint = Math.floor( point );
+
+ if ( intPoint !== oldIntPoint ) {
+
+ chunkLengths[ intPoint ] = totalLength;
+ oldIntPoint = intPoint;
+
+ }
+
+ }
+
+ // last point ends with total length
+
+ chunkLengths[ chunkLengths.length ] = totalLength;
+
+ return { chunks: chunkLengths, total: totalLength };
+
+ };
+
+ this.reparametrizeByArcLength = function ( samplingCoef ) {
+
+ var i, j,
+ index, indexCurrent, indexNext,
+ realDistance,
+ sampling, position,
+ newpoints = [],
+ tmpVec = new THREE.Vector3(),
+ sl = this.getLength();
+
+ newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() );
+
+ for ( i = 1; i < this.points.length; i ++ ) {
+
+ //tmpVec.copy( this.points[ i - 1 ] );
+ //linearDistance = tmpVec.distanceTo( this.points[ i ] );
+
+ realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ];
+
+ sampling = Math.ceil( samplingCoef * realDistance / sl.total );
+
+ indexCurrent = ( i - 1 ) / ( this.points.length - 1 );
+ indexNext = i / ( this.points.length - 1 );
+
+ for ( j = 1; j < sampling - 1; j ++ ) {
+
+ index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent );
+
+ position = this.getPoint( index );
+ newpoints.push( tmpVec.copy( position ).clone() );
+
+ }
+
+ newpoints.push( tmpVec.copy( this.points[ i ] ).clone() );
+
+ }
+
+ this.points = newpoints;
+
+ };
+
+ // Catmull-Rom
+
+ function interpolate( p0, p1, p2, p3, t, t2, t3 ) {
+
+ var v0 = ( p2 - p0 ) * 0.5,
+ v1 = ( p3 - p1 ) * 0.5;
+
+ return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
+
+ }
+
+};
+
+// File:src/math/Triangle.js
+
+/**
+ * @author bhouston / http://clara.io
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Triangle = function ( a, b, c ) {
+
+ this.a = ( a !== undefined ) ? a : new THREE.Vector3();
+ this.b = ( b !== undefined ) ? b : new THREE.Vector3();
+ this.c = ( c !== undefined ) ? c : new THREE.Vector3();
+
+};
+
+THREE.Triangle.normal = function () {
+
+ var v0 = new THREE.Vector3();
+
+ return function ( a, b, c, optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ result.subVectors( c, b );
+ v0.subVectors( a, b );
+ result.cross( v0 );
+
+ var resultLengthSq = result.lengthSq();
+ if ( resultLengthSq > 0 ) {
+
+ return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) );
+
+ }
+
+ return result.set( 0, 0, 0 );
+
+ };
+
+}();
+
+// static/instance method to calculate barycentric coordinates
+// based on: http://www.blackpawn.com/texts/pointinpoly/default.html
+THREE.Triangle.barycoordFromPoint = function () {
+
+ var v0 = new THREE.Vector3();
+ var v1 = new THREE.Vector3();
+ var v2 = new THREE.Vector3();
+
+ return function ( point, a, b, c, optionalTarget ) {
+
+ v0.subVectors( c, a );
+ v1.subVectors( b, a );
+ v2.subVectors( point, a );
+
+ 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 denom = ( dot00 * dot11 - dot01 * dot01 );
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ // collinear or singular triangle
+ if ( denom === 0 ) {
+
+ // arbitrary location outside of triangle?
+ // not sure if this is the best idea, maybe should be returning undefined
+ return result.set( - 2, - 1, - 1 );
+
+ }
+
+ var invDenom = 1 / denom;
+ var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
+ var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
+
+ // barycentric coordinates must always sum to 1
+ return result.set( 1 - u - v, v, u );
+
+ };
+
+}();
+
+THREE.Triangle.containsPoint = function () {
+
+ var v1 = new THREE.Vector3();
+
+ return function ( point, a, b, c ) {
+
+ var result = THREE.Triangle.barycoordFromPoint( point, a, b, c, v1 );
+
+ return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 );
+
+ };
+
+}();
+
+THREE.Triangle.prototype = {
+
+ constructor: THREE.Triangle,
+
+ set: function ( a, b, c ) {
+
+ this.a.copy( a );
+ this.b.copy( b );
+ this.c.copy( c );
+
+ return this;
+
+ },
+
+ setFromPointsAndIndices: function ( points, i0, i1, i2 ) {
+
+ this.a.copy( points[ i0 ] );
+ this.b.copy( points[ i1 ] );
+ this.c.copy( points[ i2 ] );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( triangle ) {
+
+ this.a.copy( triangle.a );
+ this.b.copy( triangle.b );
+ this.c.copy( triangle.c );
+
+ return this;
+
+ },
+
+ area: function () {
+
+ var v0 = new THREE.Vector3();
+ var v1 = new THREE.Vector3();
+
+ return function () {
+
+ v0.subVectors( this.c, this.b );
+ v1.subVectors( this.a, this.b );
+
+ return v0.cross( v1 ).length() * 0.5;
+
+ };
+
+ }(),
+
+ midpoint: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+ return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 );
+
+ },
+
+ normal: function ( optionalTarget ) {
+
+ return THREE.Triangle.normal( this.a, this.b, this.c, optionalTarget );
+
+ },
+
+ plane: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Plane();
+
+ return result.setFromCoplanarPoints( this.a, this.b, this.c );
+
+ },
+
+ barycoordFromPoint: function ( point, optionalTarget ) {
+
+ return THREE.Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget );
+
+ },
+
+ containsPoint: function ( point ) {
+
+ return THREE.Triangle.containsPoint( point, this.a, this.b, this.c );
+
+ },
+
+ equals: function ( triangle ) {
+
+ return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c );
+
+ }
+
+};
+
+// File:src/core/Channels.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Channels = function () {
+
+ this.mask = 1;
+
+};
+
+THREE.Channels.prototype = {
+
+ constructor: THREE.Channels,
+
+ set: function ( channel ) {
+
+ this.mask = 1 << channel;
+
+ },
+
+ enable: function ( channel ) {
+
+ this.mask |= 1 << channel;
+
+ },
+
+ toggle: function ( channel ) {
+
+ this.mask ^= 1 << channel;
+
+ },
+
+ disable: function ( channel ) {
+
+ this.mask &= ~ ( 1 << channel );
+
+ }
+
+};
+
+// File:src/core/Clock.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Clock = function ( autoStart ) {
+
+ this.autoStart = ( autoStart !== undefined ) ? autoStart : true;
+
+ this.startTime = 0;
+ this.oldTime = 0;
+ this.elapsedTime = 0;
+
+ this.running = false;
+
+};
+
+THREE.Clock.prototype = {
+
+ constructor: THREE.Clock,
+
+ start: function () {
+
+ this.startTime = self.performance.now();
+
+ this.oldTime = this.startTime;
+ this.running = true;
+
+ },
+
+ stop: function () {
+
+ this.getElapsedTime();
+ this.running = false;
+
+ },
+
+ getElapsedTime: function () {
+
+ this.getDelta();
+ return this.elapsedTime;
+
+ },
+
+ getDelta: function () {
+
+ var diff = 0;
+
+ if ( this.autoStart && ! this.running ) {
+
+ this.start();
+
+ }
+
+ if ( this.running ) {
+
+ var newTime = self.performance.now();
+
+ diff = 0.001 * ( newTime - this.oldTime );
+ this.oldTime = newTime;
+
+ this.elapsedTime += diff;
+
+ }
+
+ return diff;
+
+ }
+
+};
+
+// File:src/core/EventDispatcher.js
+
+/**
+ * https://github.com/mrdoob/eventdispatcher.js/
+ */
+
+THREE.EventDispatcher = function () {};
+
+THREE.EventDispatcher.prototype = {
+
+ constructor: THREE.EventDispatcher,
+
+ apply: function ( object ) {
+
+ object.addEventListener = THREE.EventDispatcher.prototype.addEventListener;
+ object.hasEventListener = THREE.EventDispatcher.prototype.hasEventListener;
+ object.removeEventListener = THREE.EventDispatcher.prototype.removeEventListener;
+ object.dispatchEvent = THREE.EventDispatcher.prototype.dispatchEvent;
+
+ },
+
+ addEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) this._listeners = {};
+
+ var listeners = this._listeners;
+
+ if ( listeners[ type ] === undefined ) {
+
+ listeners[ type ] = [];
+
+ }
+
+ if ( listeners[ type ].indexOf( listener ) === - 1 ) {
+
+ listeners[ type ].push( listener );
+
+ }
+
+ },
+
+ hasEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) return false;
+
+ var listeners = this._listeners;
+
+ if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {
+
+ return true;
+
+ }
+
+ return false;
+
+ },
+
+ removeEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) return;
+
+ var listeners = this._listeners;
+ var listenerArray = listeners[ type ];
+
+ if ( listenerArray !== undefined ) {
+
+ var index = listenerArray.indexOf( listener );
+
+ if ( index !== - 1 ) {
+
+ listenerArray.splice( index, 1 );
+
+ }
+
+ }
+
+ },
+
+ dispatchEvent: function ( event ) {
+
+ if ( this._listeners === undefined ) return;
+
+ var listeners = this._listeners;
+ var listenerArray = listeners[ event.type ];
+
+ if ( listenerArray !== undefined ) {
+
+ event.target = this;
+
+ var array = [];
+ var length = listenerArray.length;
+
+ for ( var i = 0; i < length; i ++ ) {
+
+ array[ i ] = listenerArray[ i ];
+
+ }
+
+ for ( var i = 0; i < length; i ++ ) {
+
+ array[ i ].call( this, event );
+
+ }
+
+ }
+
+ }
+
+};
+
+// File:src/core/Raycaster.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author bhouston / http://clara.io/
+ * @author stephomi / http://stephaneginier.com/
+ */
+
+( function ( THREE ) {
+
+ THREE.Raycaster = function ( origin, direction, near, far ) {
+
+ this.ray = new THREE.Ray( origin, direction );
+ // direction is assumed to be normalized (for accurate distance calculations)
+
+ this.near = near || 0;
+ this.far = far || 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 descSort( a, b ) {
+
+ return a.distance - b.distance;
+
+ }
+
+ function intersectObject( object, raycaster, intersects, recursive ) {
+
+ if ( object.visible === false ) return;
+
+ object.raycast( raycaster, intersects );
+
+ if ( recursive === true ) {
+
+ var children = object.children;
+
+ for ( var i = 0, l = children.length; i < l; i ++ ) {
+
+ intersectObject( children[ i ], raycaster, intersects, true );
+
+ }
+
+ }
+
+ }
+
+ //
+
+ THREE.Raycaster.prototype = {
+
+ constructor: THREE.Raycaster,
+
+ linePrecision: 1,
+
+ set: function ( origin, direction ) {
+
+ // direction is assumed to be normalized (for accurate distance calculations)
+
+ this.ray.set( origin, direction );
+
+ },
+
+ setFromCamera: function ( coords, camera ) {
+
+ if ( camera instanceof THREE.PerspectiveCamera ) {
+
+ this.ray.origin.setFromMatrixPosition( camera.matrixWorld );
+ this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize();
+
+ } else if ( camera instanceof THREE.OrthographicCamera ) {
+
+ this.ray.origin.set( coords.x, coords.y, - 1 ).unproject( camera );
+ this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );
+
+ } else {
+
+ console.error( 'THREE.Raycaster: Unsupported camera type.' );
+
+ }
+
+ },
+
+ intersectObject: function ( object, recursive ) {
+
+ var intersects = [];
+
+ intersectObject( object, this, intersects, recursive );
+
+ intersects.sort( descSort );
+
+ return intersects;
+
+ },
+
+ intersectObjects: function ( objects, recursive ) {
+
+ var intersects = [];
+
+ if ( Array.isArray( objects ) === false ) {
+
+ console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' );
+ return intersects;
+
+ }
+
+ for ( var i = 0, l = objects.length; i < l; i ++ ) {
+
+ intersectObject( objects[ i ], this, intersects, recursive );
+
+ }
+
+ intersects.sort( descSort );
+
+ return intersects;
+
+ }
+
+ };
+
+}( THREE ) );
+
+// File:src/core/Object3D.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author elephantatwork / www.elephantatwork.ch
+ */
+
+THREE.Object3D = function () {
+
+ Object.defineProperty( this, 'id', { value: THREE.Object3DIdCount ++ } );
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.name = '';
+ this.type = 'Object3D';
+
+ this.parent = null;
+ this.channels = new THREE.Channels();
+ this.children = [];
+
+ this.up = THREE.Object3D.DefaultUp.clone();
+
+ var position = new THREE.Vector3();
+ var rotation = new THREE.Euler();
+ var quaternion = new THREE.Quaternion();
+ var scale = new THREE.Vector3( 1, 1, 1 );
+
+ function onRotationChange() {
+
+ quaternion.setFromEuler( rotation, false );
+
+ }
+
+ function onQuaternionChange() {
+
+ rotation.setFromQuaternion( quaternion, undefined, false );
+
+ }
+
+ rotation.onChange( onRotationChange );
+ quaternion.onChange( onQuaternionChange );
+
+ Object.defineProperties( this, {
+ position: {
+ enumerable: true,
+ value: position
+ },
+ rotation: {
+ enumerable: true,
+ value: rotation
+ },
+ quaternion: {
+ enumerable: true,
+ value: quaternion
+ },
+ scale: {
+ enumerable: true,
+ value: scale
+ },
+ modelViewMatrix: {
+ value: new THREE.Matrix4()
+ },
+ normalMatrix: {
+ value: new THREE.Matrix3()
+ }
+ } );
+
+ this.rotationAutoUpdate = true;
+
+ this.matrix = new THREE.Matrix4();
+ this.matrixWorld = new THREE.Matrix4();
+
+ this.matrixAutoUpdate = THREE.Object3D.DefaultMatrixAutoUpdate;
+ this.matrixWorldNeedsUpdate = false;
+
+ this.visible = true;
+
+ this.castShadow = false;
+ this.receiveShadow = false;
+
+ this.frustumCulled = true;
+ this.renderOrder = 0;
+
+ this.userData = {};
+
+};
+
+THREE.Object3D.DefaultUp = new THREE.Vector3( 0, 1, 0 );
+THREE.Object3D.DefaultMatrixAutoUpdate = true;
+
+THREE.Object3D.prototype = {
+
+ constructor: THREE.Object3D,
+
+ get eulerOrder () {
+
+ console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' );
+
+ return this.rotation.order;
+
+ },
+
+ set eulerOrder ( value ) {
+
+ console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' );
+
+ this.rotation.order = value;
+
+ },
+
+ get useQuaternion () {
+
+ console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );
+
+ },
+
+ set useQuaternion ( value ) {
+
+ console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );
+
+ },
+
+ set renderDepth ( value ) {
+
+ console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' );
+
+ },
+
+ //
+
+ applyMatrix: function ( matrix ) {
+
+ this.matrix.multiplyMatrices( matrix, this.matrix );
+
+ this.matrix.decompose( this.position, this.quaternion, this.scale );
+
+ },
+
+ setRotationFromAxisAngle: function ( axis, angle ) {
+
+ // assumes axis is normalized
+
+ this.quaternion.setFromAxisAngle( axis, angle );
+
+ },
+
+ setRotationFromEuler: function ( euler ) {
+
+ this.quaternion.setFromEuler( euler, true );
+
+ },
+
+ setRotationFromMatrix: function ( m ) {
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ this.quaternion.setFromRotationMatrix( m );
+
+ },
+
+ setRotationFromQuaternion: function ( q ) {
+
+ // assumes q is normalized
+
+ this.quaternion.copy( q );
+
+ },
+
+ rotateOnAxis: function () {
+
+ // rotate object on axis in object space
+ // axis is assumed to be normalized
+
+ var q1 = new THREE.Quaternion();
+
+ return function ( axis, angle ) {
+
+ q1.setFromAxisAngle( axis, angle );
+
+ this.quaternion.multiply( q1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ rotateX: function () {
+
+ var v1 = new THREE.Vector3( 1, 0, 0 );
+
+ return function ( angle ) {
+
+ return this.rotateOnAxis( v1, angle );
+
+ };
+
+ }(),
+
+ rotateY: function () {
+
+ var v1 = new THREE.Vector3( 0, 1, 0 );
+
+ return function ( angle ) {
+
+ return this.rotateOnAxis( v1, angle );
+
+ };
+
+ }(),
+
+ rotateZ: function () {
+
+ var v1 = new THREE.Vector3( 0, 0, 1 );
+
+ return function ( angle ) {
+
+ return this.rotateOnAxis( v1, angle );
+
+ };
+
+ }(),
+
+ translateOnAxis: function () {
+
+ // translate object by distance along axis in object space
+ // axis is assumed to be normalized
+
+ var v1 = new THREE.Vector3();
+
+ return function ( axis, distance ) {
+
+ v1.copy( axis ).applyQuaternion( this.quaternion );
+
+ this.position.add( v1.multiplyScalar( distance ) );
+
+ return this;
+
+ };
+
+ }(),
+
+ translate: function ( distance, axis ) {
+
+ console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' );
+ return this.translateOnAxis( axis, distance );
+
+ },
+
+ translateX: function () {
+
+ var v1 = new THREE.Vector3( 1, 0, 0 );
+
+ return function ( distance ) {
+
+ return this.translateOnAxis( v1, distance );
+
+ };
+
+ }(),
+
+ translateY: function () {
+
+ var v1 = new THREE.Vector3( 0, 1, 0 );
+
+ return function ( distance ) {
+
+ return this.translateOnAxis( v1, distance );
+
+ };
+
+ }(),
+
+ translateZ: function () {
+
+ var v1 = new THREE.Vector3( 0, 0, 1 );
+
+ return function ( distance ) {
+
+ return this.translateOnAxis( v1, distance );
+
+ };
+
+ }(),
+
+ localToWorld: function ( vector ) {
+
+ return vector.applyMatrix4( this.matrixWorld );
+
+ },
+
+ worldToLocal: function () {
+
+ var m1 = new THREE.Matrix4();
+
+ return function ( vector ) {
+
+ return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) );
+
+ };
+
+ }(),
+
+ lookAt: function () {
+
+ // This routine does not support objects with rotated and/or translated parent(s)
+
+ var m1 = new THREE.Matrix4();
+
+ return function ( vector ) {
+
+ m1.lookAt( vector, this.position, this.up );
+
+ this.quaternion.setFromRotationMatrix( m1 );
+
+ };
+
+ }(),
+
+ add: function ( object ) {
+
+ if ( arguments.length > 1 ) {
+
+ for ( var i = 0; i < arguments.length; i ++ ) {
+
+ this.add( arguments[ i ] );
+
+ }
+
+ return this;
+
+ }
+
+ if ( object === this ) {
+
+ console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object );
+ return this;
+
+ }
+
+ if ( object instanceof THREE.Object3D ) {
+
+ if ( object.parent !== null ) {
+
+ object.parent.remove( object );
+
+ }
+
+ object.parent = this;
+ object.dispatchEvent( { type: 'added' } );
+
+ this.children.push( object );
+
+ } else {
+
+ console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object );
+
+ }
+
+ return this;
+
+ },
+
+ remove: function ( object ) {
+
+ if ( arguments.length > 1 ) {
+
+ for ( var i = 0; i < arguments.length; i ++ ) {
+
+ this.remove( arguments[ i ] );
+
+ }
+
+ }
+
+ var index = this.children.indexOf( object );
+
+ if ( index !== - 1 ) {
+
+ object.parent = null;
+
+ object.dispatchEvent( { type: 'removed' } );
+
+ this.children.splice( index, 1 );
+
+ }
+
+ },
+
+ getChildByName: function ( name ) {
+
+ console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' );
+ return this.getObjectByName( name );
+
+ },
+
+ getObjectById: function ( id ) {
+
+ return this.getObjectByProperty( 'id', id );
+
+ },
+
+ getObjectByName: function ( name ) {
+
+ return this.getObjectByProperty( 'name', name );
+
+ },
+
+ getObjectByProperty: function ( name, value ) {
+
+ if ( this[ name ] === value ) return this;
+
+ for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+ var child = this.children[ i ];
+ var object = child.getObjectByProperty( name, value );
+
+ if ( object !== undefined ) {
+
+ return object;
+
+ }
+
+ }
+
+ return undefined;
+
+ },
+
+ getWorldPosition: function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ this.updateMatrixWorld( true );
+
+ return result.setFromMatrixPosition( this.matrixWorld );
+
+ },
+
+ getWorldQuaternion: function () {
+
+ var position = new THREE.Vector3();
+ var scale = new THREE.Vector3();
+
+ return function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Quaternion();
+
+ this.updateMatrixWorld( true );
+
+ this.matrixWorld.decompose( position, result, scale );
+
+ return result;
+
+ };
+
+ }(),
+
+ getWorldRotation: function () {
+
+ var quaternion = new THREE.Quaternion();
+
+ return function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Euler();
+
+ this.getWorldQuaternion( quaternion );
+
+ return result.setFromQuaternion( quaternion, this.rotation.order, false );
+
+ };
+
+ }(),
+
+ getWorldScale: function () {
+
+ var position = new THREE.Vector3();
+ var quaternion = new THREE.Quaternion();
+
+ return function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ this.updateMatrixWorld( true );
+
+ this.matrixWorld.decompose( position, quaternion, result );
+
+ return result;
+
+ };
+
+ }(),
+
+ getWorldDirection: function () {
+
+ var quaternion = new THREE.Quaternion();
+
+ return function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ this.getWorldQuaternion( quaternion );
+
+ return result.set( 0, 0, 1 ).applyQuaternion( quaternion );
+
+ };
+
+ }(),
+
+ raycast: function () {},
+
+ traverse: function ( callback ) {
+
+ callback( this );
+
+ var children = this.children;
+
+ for ( var i = 0, l = children.length; i < l; i ++ ) {
+
+ children[ i ].traverse( callback );
+
+ }
+
+ },
+
+ traverseVisible: function ( callback ) {
+
+ if ( this.visible === false ) return;
+
+ callback( this );
+
+ var children = this.children;
+
+ for ( var i = 0, l = children.length; i < l; i ++ ) {
+
+ children[ i ].traverseVisible( callback );
+
+ }
+
+ },
+
+ traverseAncestors: function ( callback ) {
+
+ var parent = this.parent;
+
+ if ( parent !== null ) {
+
+ callback( parent );
+
+ parent.traverseAncestors( callback );
+
+ }
+
+ },
+
+ updateMatrix: function () {
+
+ this.matrix.compose( this.position, this.quaternion, this.scale );
+
+ this.matrixWorldNeedsUpdate = true;
+
+ },
+
+ updateMatrixWorld: function ( force ) {
+
+ if ( this.matrixAutoUpdate === true ) this.updateMatrix();
+
+ if ( this.matrixWorldNeedsUpdate === true || force === true ) {
+
+ if ( this.parent === null ) {
+
+ this.matrixWorld.copy( this.matrix );
+
+ } else {
+
+ this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
+
+ }
+
+ this.matrixWorldNeedsUpdate = false;
+
+ force = true;
+
+ }
+
+ // update children
+
+ for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+ this.children[ i ].updateMatrixWorld( force );
+
+ }
+
+ },
+
+ toJSON: function ( meta ) {
+
+ var isRootObject = ( meta === undefined );
+
+ var output = {};
+
+ // meta is a hash used to collect geometries, materials.
+ // not providing it implies that this is the root object
+ // being serialized.
+ if ( isRootObject ) {
+
+ // initialize meta obj
+ meta = {
+ geometries: {},
+ materials: {},
+ textures: {},
+ images: {}
+ };
+
+ output.metadata = {
+ version: 4.4,
+ type: 'Object',
+ generator: 'Object3D.toJSON'
+ };
+
+ }
+
+ // standard Object3D serialization
+
+ var object = {};
+
+ object.uuid = this.uuid;
+ object.type = this.type;
+
+ if ( this.name !== '' ) object.name = this.name;
+ if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
+ if ( this.castShadow === true ) object.castShadow = true;
+ if ( this.receiveShadow === true ) object.receiveShadow = true;
+ if ( this.visible === false ) object.visible = false;
+
+ object.matrix = this.matrix.toArray();
+
+ //
+
+ if ( this.geometry !== undefined ) {
+
+ if ( meta.geometries[ this.geometry.uuid ] === undefined ) {
+
+ meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON( meta );
+
+ }
+
+ object.geometry = this.geometry.uuid;
+
+ }
+
+ if ( this.material !== undefined ) {
+
+ if ( meta.materials[ this.material.uuid ] === undefined ) {
+
+ meta.materials[ this.material.uuid ] = this.material.toJSON( meta );
+
+ }
+
+ object.material = this.material.uuid;
+
+ }
+
+ //
+
+ if ( this.children.length > 0 ) {
+
+ object.children = [];
+
+ for ( var i = 0; i < this.children.length; i ++ ) {
+
+ object.children.push( this.children[ i ].toJSON( meta ).object );
+
+ }
+
+ }
+
+ if ( isRootObject ) {
+
+ var geometries = extractFromCache( meta.geometries );
+ var materials = extractFromCache( meta.materials );
+ var textures = extractFromCache( meta.textures );
+ var images = extractFromCache( meta.images );
+
+ if ( geometries.length > 0 ) output.geometries = geometries;
+ if ( materials.length > 0 ) output.materials = materials;
+ if ( textures.length > 0 ) output.textures = textures;
+ if ( images.length > 0 ) output.images = images;
+
+ }
+
+ output.object = object;
+
+ return output;
+
+ // extract data from the cache hash
+ // remove metadata on each item
+ // and return as array
+ function extractFromCache ( cache ) {
+
+ var values = [];
+ for ( var key in cache ) {
+
+ var data = cache[ key ];
+ delete data.metadata;
+ values.push( data );
+
+ }
+ return values;
+
+ }
+
+ },
+
+ clone: function ( recursive ) {
+
+ return new this.constructor().copy( this, recursive );
+
+ },
+
+ copy: function ( source, recursive ) {
+
+ if ( recursive === undefined ) recursive = true;
+
+ this.name = source.name;
+
+ this.up.copy( source.up );
+
+ this.position.copy( source.position );
+ this.quaternion.copy( source.quaternion );
+ this.scale.copy( source.scale );
+
+ this.rotationAutoUpdate = source.rotationAutoUpdate;
+
+ this.matrix.copy( source.matrix );
+ this.matrixWorld.copy( source.matrixWorld );
+
+ this.matrixAutoUpdate = source.matrixAutoUpdate;
+ this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
+
+ this.visible = source.visible;
+
+ this.castShadow = source.castShadow;
+ this.receiveShadow = source.receiveShadow;
+
+ this.frustumCulled = source.frustumCulled;
+ this.renderOrder = source.renderOrder;
+
+ this.userData = JSON.parse( JSON.stringify( source.userData ) );
+
+ if ( recursive === true ) {
+
+ for ( var i = 0; i < source.children.length; i ++ ) {
+
+ var child = source.children[ i ];
+ this.add( child.clone() );
+
+ }
+
+ }
+
+ return this;
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.Object3D.prototype );
+
+THREE.Object3DIdCount = 0;
+
+// File:src/core/Face3.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) {
+
+ this.a = a;
+ this.b = b;
+ this.c = c;
+
+ this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3();
+ this.vertexNormals = Array.isArray( normal ) ? normal : [];
+
+ this.color = color instanceof THREE.Color ? color : new THREE.Color();
+ this.vertexColors = Array.isArray( color ) ? color : [];
+
+ this.materialIndex = materialIndex !== undefined ? materialIndex : 0;
+
+};
+
+THREE.Face3.prototype = {
+
+ constructor: THREE.Face3,
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.a = source.a;
+ this.b = source.b;
+ this.c = source.c;
+
+ this.normal.copy( source.normal );
+ this.color.copy( source.color );
+
+ this.materialIndex = source.materialIndex;
+
+ for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) {
+
+ this.vertexNormals[ i ] = source.vertexNormals[ i ].clone();
+
+ }
+
+ for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) {
+
+ this.vertexColors[ i ] = source.vertexColors[ i ].clone();
+
+ }
+
+ return this;
+
+ }
+
+};
+
+// File:src/core/Face4.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) {
+
+ console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' );
+ return new THREE.Face3( a, b, c, normal, color, materialIndex );
+
+};
+
+// File:src/core/BufferAttribute.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.BufferAttribute = function ( array, itemSize ) {
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.array = array;
+ this.itemSize = itemSize;
+
+ this.dynamic = false;
+ this.updateRange = { offset: 0, count: - 1 };
+
+ this.version = 0;
+
+};
+
+THREE.BufferAttribute.prototype = {
+
+ constructor: THREE.BufferAttribute,
+
+ get length() {
+
+ console.warn( 'THREE.BufferAttribute: .length has been deprecated. Please use .count.' );
+ return this.array.length;
+
+ },
+
+ get count() {
+
+ return this.array.length / this.itemSize;
+
+ },
+
+ set needsUpdate( value ) {
+
+ if ( value === true ) this.version ++;
+
+ },
+
+ setDynamic: function ( value ) {
+
+ this.dynamic = value;
+
+ return this;
+
+ },
+
+ copy: function ( source ) {
+
+ this.array = new source.array.constructor( source.array );
+ this.itemSize = source.itemSize;
+
+ this.dynamic = source.dynamic;
+
+ return this;
+
+ },
+
+ copyAt: function ( index1, attribute, index2 ) {
+
+ index1 *= this.itemSize;
+ index2 *= attribute.itemSize;
+
+ for ( var i = 0, l = this.itemSize; i < l; i ++ ) {
+
+ this.array[ index1 + i ] = attribute.array[ index2 + i ];
+
+ }
+
+ return this;
+
+ },
+
+ copyArray: function ( array ) {
+
+ this.array.set( array );
+
+ return this;
+
+ },
+
+ copyColorsArray: function ( colors ) {
+
+ var array = this.array, offset = 0;
+
+ for ( var i = 0, l = colors.length; i < l; i ++ ) {
+
+ var color = colors[ i ];
+
+ if ( color === undefined ) {
+
+ console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i );
+ color = new THREE.Color();
+
+ }
+
+ array[ offset ++ ] = color.r;
+ array[ offset ++ ] = color.g;
+ array[ offset ++ ] = color.b;
+
+ }
+
+ return this;
+
+ },
+
+ copyIndicesArray: function ( indices ) {
+
+ var array = this.array, offset = 0;
+
+ for ( var i = 0, l = indices.length; i < l; i ++ ) {
+
+ var index = indices[ i ];
+
+ array[ offset ++ ] = index.a;
+ array[ offset ++ ] = index.b;
+ array[ offset ++ ] = index.c;
+
+ }
+
+ return this;
+
+ },
+
+ copyVector2sArray: function ( vectors ) {
+
+ var array = this.array, offset = 0;
+
+ for ( var i = 0, l = vectors.length; i < l; i ++ ) {
+
+ var vector = vectors[ i ];
+
+ if ( vector === undefined ) {
+
+ console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i );
+ vector = new THREE.Vector2();
+
+ }
+
+ array[ offset ++ ] = vector.x;
+ array[ offset ++ ] = vector.y;
+
+ }
+
+ return this;
+
+ },
+
+ copyVector3sArray: function ( vectors ) {
+
+ var array = this.array, offset = 0;
+
+ for ( var i = 0, l = vectors.length; i < l; i ++ ) {
+
+ var vector = vectors[ i ];
+
+ if ( vector === undefined ) {
+
+ console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i );
+ vector = new THREE.Vector3();
+
+ }
+
+ array[ offset ++ ] = vector.x;
+ array[ offset ++ ] = vector.y;
+ array[ offset ++ ] = vector.z;
+
+ }
+
+ return this;
+
+ },
+
+ copyVector4sArray: function ( vectors ) {
+
+ var array = this.array, offset = 0;
+
+ for ( var i = 0, l = vectors.length; i < l; i ++ ) {
+
+ var vector = vectors[ i ];
+
+ if ( vector === undefined ) {
+
+ console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i );
+ vector = new THREE.Vector4();
+
+ }
+
+ array[ offset ++ ] = vector.x;
+ array[ offset ++ ] = vector.y;
+ array[ offset ++ ] = vector.z;
+ array[ offset ++ ] = vector.w;
+
+ }
+
+ return this;
+
+ },
+
+ set: function ( value, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.array.set( value, offset );
+
+ return this;
+
+ },
+
+ getX: function ( index ) {
+
+ return this.array[ index * this.itemSize ];
+
+ },
+
+ setX: function ( index, x ) {
+
+ this.array[ index * this.itemSize ] = x;
+
+ return this;
+
+ },
+
+ getY: function ( index ) {
+
+ return this.array[ index * this.itemSize + 1 ];
+
+ },
+
+ setY: function ( index, y ) {
+
+ this.array[ index * this.itemSize + 1 ] = y;
+
+ return this;
+
+ },
+
+ getZ: function ( index ) {
+
+ return this.array[ index * this.itemSize + 2 ];
+
+ },
+
+ setZ: function ( index, z ) {
+
+ this.array[ index * this.itemSize + 2 ] = z;
+
+ return this;
+
+ },
+
+ getW: function ( index ) {
+
+ return this.array[ index * this.itemSize + 3 ];
+
+ },
+
+ setW: function ( index, w ) {
+
+ this.array[ index * this.itemSize + 3 ] = w;
+
+ return this;
+
+ },
+
+ setXY: function ( index, x, y ) {
+
+ index *= this.itemSize;
+
+ this.array[ index + 0 ] = x;
+ this.array[ index + 1 ] = y;
+
+ return this;
+
+ },
+
+ setXYZ: function ( index, x, y, z ) {
+
+ index *= this.itemSize;
+
+ this.array[ index + 0 ] = x;
+ this.array[ index + 1 ] = y;
+ this.array[ index + 2 ] = z;
+
+ return this;
+
+ },
+
+ setXYZW: function ( index, x, y, z, w ) {
+
+ index *= this.itemSize;
+
+ this.array[ index + 0 ] = x;
+ this.array[ index + 1 ] = y;
+ this.array[ index + 2 ] = z;
+ this.array[ index + 3 ] = w;
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ }
+
+};
+
+//
+
+THREE.Int8Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Int8Array( array ), itemSize );
+
+};
+
+THREE.Uint8Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Uint8Array( array ), itemSize );
+
+};
+
+THREE.Uint8ClampedAttribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Uint8ClampedArray( array ), itemSize );
+
+};
+
+THREE.Int16Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Int16Array( array ), itemSize );
+
+};
+
+THREE.Uint16Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Uint16Array( array ), itemSize );
+
+};
+
+THREE.Int32Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Int32Array( array ), itemSize );
+
+};
+
+THREE.Uint32Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Uint32Array( array ), itemSize );
+
+};
+
+THREE.Float32Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Float32Array( array ), itemSize );
+
+};
+
+THREE.Float64Attribute = function ( array, itemSize ) {
+
+ return new THREE.BufferAttribute( new Float64Array( array ), itemSize );
+
+};
+
+
+// Deprecated
+
+THREE.DynamicBufferAttribute = function ( array, itemSize ) {
+
+ console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' );
+ return new THREE.BufferAttribute( array, itemSize ).setDynamic( true );
+
+};
+
+// File:src/core/InstancedBufferAttribute.js
+
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InstancedBufferAttribute = function ( array, itemSize, meshPerAttribute ) {
+
+ THREE.BufferAttribute.call( this, array, itemSize );
+
+ this.meshPerAttribute = meshPerAttribute || 1;
+
+};
+
+THREE.InstancedBufferAttribute.prototype = Object.create( THREE.BufferAttribute.prototype );
+THREE.InstancedBufferAttribute.prototype.constructor = THREE.InstancedBufferAttribute;
+
+THREE.InstancedBufferAttribute.prototype.copy = function ( source ) {
+
+ THREE.BufferAttribute.prototype.copy.call( this, source );
+
+ this.meshPerAttribute = source.meshPerAttribute;
+
+ return this;
+
+};
+
+// File:src/core/InterleavedBuffer.js
+
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InterleavedBuffer = function ( array, stride ) {
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.array = array;
+ this.stride = stride;
+
+ this.dynamic = false;
+ this.updateRange = { offset: 0, count: - 1 };
+
+ this.version = 0;
+
+};
+
+THREE.InterleavedBuffer.prototype = {
+
+ constructor: THREE.InterleavedBuffer,
+
+ get length () {
+
+ return this.array.length;
+
+ },
+
+ get count () {
+
+ return this.array.length / this.stride;
+
+ },
+
+ set needsUpdate( value ) {
+
+ if ( value === true ) this.version ++;
+
+ },
+
+ setDynamic: function ( value ) {
+
+ this.dynamic = value;
+
+ return this;
+
+ },
+
+ copy: function ( source ) {
+
+ this.array = new source.array.constructor( source.array );
+ this.stride = source.stride;
+ this.dynamic = source.dynamic;
+
+ },
+
+ copyAt: function ( index1, attribute, index2 ) {
+
+ index1 *= this.stride;
+ index2 *= attribute.stride;
+
+ for ( var i = 0, l = this.stride; i < l; i ++ ) {
+
+ this.array[ index1 + i ] = attribute.array[ index2 + i ];
+
+ }
+
+ return this;
+
+ },
+
+ set: function ( value, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.array.set( value, offset );
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ }
+
+};
+
+// File:src/core/InstancedInterleavedBuffer.js
+
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InstancedInterleavedBuffer = function ( array, stride, meshPerAttribute ) {
+
+ THREE.InterleavedBuffer.call( this, array, stride );
+
+ this.meshPerAttribute = meshPerAttribute || 1;
+
+};
+
+THREE.InstancedInterleavedBuffer.prototype = Object.create( THREE.InterleavedBuffer.prototype );
+THREE.InstancedInterleavedBuffer.prototype.constructor = THREE.InstancedInterleavedBuffer;
+
+THREE.InstancedInterleavedBuffer.prototype.copy = function ( source ) {
+
+ THREE.InterleavedBuffer.prototype.copy.call( this, source );
+
+ this.meshPerAttribute = source.meshPerAttribute;
+
+ return this;
+
+};
+
+// File:src/core/InterleavedBufferAttribute.js
+
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InterleavedBufferAttribute = function ( interleavedBuffer, itemSize, offset ) {
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.data = interleavedBuffer;
+ this.itemSize = itemSize;
+ this.offset = offset;
+
+};
+
+
+THREE.InterleavedBufferAttribute.prototype = {
+
+ constructor: THREE.InterleavedBufferAttribute,
+
+ get length() {
+
+ console.warn( 'THREE.BufferAttribute: .length has been deprecated. Please use .count.' );
+ return this.array.length;
+
+ },
+
+ get count() {
+
+ return this.data.array.length / this.data.stride;
+
+ },
+
+ setX: function ( index, x ) {
+
+ this.data.array[ index * this.data.stride + this.offset ] = x;
+
+ return this;
+
+ },
+
+ setY: function ( index, y ) {
+
+ this.data.array[ index * this.data.stride + this.offset + 1 ] = y;
+
+ return this;
+
+ },
+
+ setZ: function ( index, z ) {
+
+ this.data.array[ index * this.data.stride + this.offset + 2 ] = z;
+
+ return this;
+
+ },
+
+ setW: function ( index, w ) {
+
+ this.data.array[ index * this.data.stride + this.offset + 3 ] = w;
+
+ return this;
+
+ },
+
+ getX: function ( index ) {
+
+ return this.data.array[ index * this.data.stride + this.offset ];
+
+ },
+
+ getY: function ( index ) {
+
+ return this.data.array[ index * this.data.stride + this.offset + 1 ];
+
+ },
+
+ getZ: function ( index ) {
+
+ return this.data.array[ index * this.data.stride + this.offset + 2 ];
+
+ },
+
+ getW: function ( index ) {
+
+ return this.data.array[ index * this.data.stride + this.offset + 3 ];
+
+ },
+
+ setXY: function ( index, x, y ) {
+
+ index = index * this.data.stride + this.offset;
+
+ this.data.array[ index + 0 ] = x;
+ this.data.array[ index + 1 ] = y;
+
+ return this;
+
+ },
+
+ setXYZ: function ( index, x, y, z ) {
+
+ index = index * this.data.stride + this.offset;
+
+ this.data.array[ index + 0 ] = x;
+ this.data.array[ index + 1 ] = y;
+ this.data.array[ index + 2 ] = z;
+
+ return this;
+
+ },
+
+ setXYZW: function ( index, x, y, z, w ) {
+
+ index = index * this.data.stride + this.offset;
+
+ this.data.array[ index + 0 ] = x;
+ this.data.array[ index + 1 ] = y;
+ this.data.array[ index + 2 ] = z;
+ this.data.array[ index + 3 ] = w;
+
+ return this;
+
+ }
+
+};
+
+// File:src/core/Geometry.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author kile / http://kile.stravaganza.org/
+ * @author alteredq / http://alteredqualia.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * @author bhouston / http://clara.io
+ */
+
+THREE.Geometry = function () {
+
+ Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } );
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.name = '';
+ this.type = 'Geometry';
+
+ this.vertices = [];
+ this.colors = [];
+ this.faces = [];
+ this.faceVertexUvs = [ [] ];
+
+ this.morphTargets = [];
+ this.morphNormals = [];
+
+ this.skinWeights = [];
+ this.skinIndices = [];
+
+ this.lineDistances = [];
+
+ this.boundingBox = null;
+ this.boundingSphere = null;
+
+ // update flags
+
+ this.verticesNeedUpdate = false;
+ this.elementsNeedUpdate = false;
+ this.uvsNeedUpdate = false;
+ this.normalsNeedUpdate = false;
+ this.colorsNeedUpdate = false;
+ this.lineDistancesNeedUpdate = false;
+ this.groupsNeedUpdate = false;
+
+};
+
+THREE.Geometry.prototype = {
+
+ constructor: THREE.Geometry,
+
+ applyMatrix: function ( matrix ) {
+
+ var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );
+
+ for ( var i = 0, il = this.vertices.length; i < il; i ++ ) {
+
+ var vertex = this.vertices[ i ];
+ vertex.applyMatrix4( matrix );
+
+ }
+
+ for ( var i = 0, il = this.faces.length; i < il; i ++ ) {
+
+ var face = this.faces[ i ];
+ face.normal.applyMatrix3( normalMatrix ).normalize();
+
+ for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
+
+ face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize();
+
+ }
+
+ }
+
+ if ( this.boundingBox !== null ) {
+
+ this.computeBoundingBox();
+
+ }
+
+ if ( this.boundingSphere !== null ) {
+
+ this.computeBoundingSphere();
+
+ }
+
+ this.verticesNeedUpdate = true;
+ this.normalsNeedUpdate = true;
+
+ },
+
+ rotateX: function () {
+
+ // rotate geometry around world x-axis
+
+ var m1;
+
+ return function rotateX( angle ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeRotationX( angle );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ rotateY: function () {
+
+ // rotate geometry around world y-axis
+
+ var m1;
+
+ return function rotateY( angle ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeRotationY( angle );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ rotateZ: function () {
+
+ // rotate geometry around world z-axis
+
+ var m1;
+
+ return function rotateZ( angle ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeRotationZ( angle );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ translate: function () {
+
+ // translate geometry
+
+ var m1;
+
+ return function translate( x, y, z ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeTranslation( x, y, z );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ scale: function () {
+
+ // scale geometry
+
+ var m1;
+
+ return function scale( x, y, z ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeScale( x, y, z );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ lookAt: function () {
+
+ var obj;
+
+ return function lookAt( vector ) {
+
+ if ( obj === undefined ) obj = new THREE.Object3D();
+
+ obj.lookAt( vector );
+
+ obj.updateMatrix();
+
+ this.applyMatrix( obj.matrix );
+
+ };
+
+ }(),
+
+ fromBufferGeometry: function ( geometry ) {
+
+ var scope = this;
+
+ var indices = geometry.index !== null ? geometry.index.array : undefined;
+ var attributes = geometry.attributes;
+
+ var vertices = attributes.position.array;
+ var normals = attributes.normal !== undefined ? attributes.normal.array : undefined;
+ var colors = attributes.color !== undefined ? attributes.color.array : undefined;
+ var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined;
+ var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined;
+
+ if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = [];
+
+ var tempNormals = [];
+ var tempUVs = [];
+ var tempUVs2 = [];
+
+ for ( var i = 0, j = 0, k = 0; i < vertices.length; i += 3, j += 2, k += 4 ) {
+
+ scope.vertices.push( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) );
+
+ if ( normals !== undefined ) {
+
+ tempNormals.push( new THREE.Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) );
+
+ }
+
+ if ( colors !== undefined ) {
+
+ scope.colors.push( new THREE.Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) );
+
+ }
+
+ if ( uvs !== undefined ) {
+
+ tempUVs.push( new THREE.Vector2( uvs[ j ], uvs[ j + 1 ] ) );
+
+ }
+
+ if ( uvs2 !== undefined ) {
+
+ tempUVs2.push( new THREE.Vector2( uvs2[ j ], uvs2[ j + 1 ] ) );
+
+ }
+
+ }
+
+ function addFace( a, b, c ) {
+
+ var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : [];
+ var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : [];
+
+ var face = new THREE.Face3( a, b, c, vertexNormals, vertexColors );
+
+ scope.faces.push( face );
+
+ if ( uvs !== undefined ) {
+
+ scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] );
+
+ }
+
+ if ( uvs2 !== undefined ) {
+
+ scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] );
+
+ }
+
+ };
+
+ if ( indices !== undefined ) {
+
+ var groups = geometry.groups;
+
+ if ( groups.length > 0 ) {
+
+ for ( var i = 0; i < groups.length; i ++ ) {
+
+ var group = groups[ i ];
+
+ var start = group.start;
+ var count = group.count;
+
+ for ( var j = start, jl = start + count; j < jl; j += 3 ) {
+
+ addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ] );
+
+ }
+
+ }
+
+ } else {
+
+ for ( var i = 0; i < indices.length; i += 3 ) {
+
+ addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );
+
+ }
+
+ }
+
+ } else {
+
+ for ( var i = 0; i < vertices.length / 3; i += 3 ) {
+
+ addFace( i, i + 1, i + 2 );
+
+ }
+
+ }
+
+ this.computeFaceNormals();
+
+ if ( geometry.boundingBox !== null ) {
+
+ this.boundingBox = geometry.boundingBox.clone();
+
+ }
+
+ if ( geometry.boundingSphere !== null ) {
+
+ this.boundingSphere = geometry.boundingSphere.clone();
+
+ }
+
+ return this;
+
+ },
+
+ center: function () {
+
+ this.computeBoundingBox();
+
+ var offset = this.boundingBox.center().negate();
+
+ this.translate( offset.x, offset.y, offset.z );
+
+ return offset;
+
+ },
+
+ normalize: function () {
+
+ this.computeBoundingSphere();
+
+ var center = this.boundingSphere.center;
+ var radius = this.boundingSphere.radius;
+
+ var s = radius === 0 ? 1 : 1.0 / radius;
+
+ var matrix = new THREE.Matrix4();
+ matrix.set(
+ s, 0, 0, - s * center.x,
+ 0, s, 0, - s * center.y,
+ 0, 0, s, - s * center.z,
+ 0, 0, 0, 1
+ );
+
+ this.applyMatrix( matrix );
+
+ return this;
+
+ },
+
+ computeFaceNormals: function () {
+
+ var cb = new THREE.Vector3(), ab = new THREE.Vector3();
+
+ for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ var face = this.faces[ f ];
+
+ var vA = this.vertices[ face.a ];
+ var vB = this.vertices[ face.b ];
+ var vC = this.vertices[ face.c ];
+
+ cb.subVectors( vC, vB );
+ ab.subVectors( vA, vB );
+ cb.cross( ab );
+
+ cb.normalize();
+
+ face.normal.copy( cb );
+
+ }
+
+ },
+
+ computeVertexNormals: function ( areaWeighted ) {
+
+ var v, vl, f, fl, face, vertices;
+
+ vertices = new Array( this.vertices.length );
+
+ for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
+
+ vertices[ v ] = new THREE.Vector3();
+
+ }
+
+ if ( areaWeighted ) {
+
+ // vertex normals weighted by triangle areas
+ // http://www.iquilezles.org/www/articles/normals/normals.htm
+
+ var vA, vB, vC;
+ var cb = new THREE.Vector3(), ab = new THREE.Vector3();
+
+ for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ face = this.faces[ f ];
+
+ vA = this.vertices[ face.a ];
+ vB = this.vertices[ face.b ];
+ vC = this.vertices[ face.c ];
+
+ cb.subVectors( vC, vB );
+ ab.subVectors( vA, vB );
+ cb.cross( ab );
+
+ vertices[ face.a ].add( cb );
+ vertices[ face.b ].add( cb );
+ vertices[ face.c ].add( cb );
+
+ }
+
+ } else {
+
+ for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ face = this.faces[ f ];
+
+ vertices[ face.a ].add( face.normal );
+ vertices[ face.b ].add( face.normal );
+ vertices[ face.c ].add( face.normal );
+
+ }
+
+ }
+
+ for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
+
+ vertices[ v ].normalize();
+
+ }
+
+ for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ face = this.faces[ f ];
+
+ var vertexNormals = face.vertexNormals;
+
+ if ( vertexNormals.length === 3 ) {
+
+ vertexNormals[ 0 ].copy( vertices[ face.a ] );
+ vertexNormals[ 1 ].copy( vertices[ face.b ] );
+ vertexNormals[ 2 ].copy( vertices[ face.c ] );
+
+ } else {
+
+ vertexNormals[ 0 ] = vertices[ face.a ].clone();
+ vertexNormals[ 1 ] = vertices[ face.b ].clone();
+ vertexNormals[ 2 ] = vertices[ face.c ].clone();
+
+ }
+
+ }
+
+ },
+
+ computeMorphNormals: function () {
+
+ var i, il, f, fl, face;
+
+ // save original normals
+ // - create temp variables on first access
+ // otherwise just copy (for faster repeated calls)
+
+ for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ face = this.faces[ f ];
+
+ if ( ! face.__originalFaceNormal ) {
+
+ face.__originalFaceNormal = face.normal.clone();
+
+ } else {
+
+ face.__originalFaceNormal.copy( face.normal );
+
+ }
+
+ if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = [];
+
+ for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) {
+
+ if ( ! face.__originalVertexNormals[ i ] ) {
+
+ face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone();
+
+ } else {
+
+ face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] );
+
+ }
+
+ }
+
+ }
+
+ // use temp geometry to compute face and vertex normals for each morph
+
+ var tmpGeo = new THREE.Geometry();
+ tmpGeo.faces = this.faces;
+
+ for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) {
+
+ // create on first access
+
+ if ( ! this.morphNormals[ i ] ) {
+
+ this.morphNormals[ i ] = {};
+ this.morphNormals[ i ].faceNormals = [];
+ this.morphNormals[ i ].vertexNormals = [];
+
+ var dstNormalsFace = this.morphNormals[ i ].faceNormals;
+ var dstNormalsVertex = this.morphNormals[ i ].vertexNormals;
+
+ var faceNormal, vertexNormals;
+
+ for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ faceNormal = new THREE.Vector3();
+ vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() };
+
+ dstNormalsFace.push( faceNormal );
+ dstNormalsVertex.push( vertexNormals );
+
+ }
+
+ }
+
+ var morphNormals = this.morphNormals[ i ];
+
+ // set vertices to morph target
+
+ tmpGeo.vertices = this.morphTargets[ i ].vertices;
+
+ // compute morph normals
+
+ tmpGeo.computeFaceNormals();
+ tmpGeo.computeVertexNormals();
+
+ // store morph normals
+
+ var faceNormal, vertexNormals;
+
+ for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ face = this.faces[ f ];
+
+ faceNormal = morphNormals.faceNormals[ f ];
+ vertexNormals = morphNormals.vertexNormals[ f ];
+
+ faceNormal.copy( face.normal );
+
+ vertexNormals.a.copy( face.vertexNormals[ 0 ] );
+ vertexNormals.b.copy( face.vertexNormals[ 1 ] );
+ vertexNormals.c.copy( face.vertexNormals[ 2 ] );
+
+ }
+
+ }
+
+ // restore original normals
+
+ for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+ face = this.faces[ f ];
+
+ face.normal = face.__originalFaceNormal;
+ face.vertexNormals = face.__originalVertexNormals;
+
+ }
+
+ },
+
+ computeTangents: function () {
+
+ console.warn( 'THREE.Geometry: .computeTangents() has been removed.' );
+
+ },
+
+ computeLineDistances: function () {
+
+ var d = 0;
+ var vertices = this.vertices;
+
+ for ( var i = 0, il = vertices.length; i < il; i ++ ) {
+
+ if ( i > 0 ) {
+
+ d += vertices[ i ].distanceTo( vertices[ i - 1 ] );
+
+ }
+
+ this.lineDistances[ i ] = d;
+
+ }
+
+ },
+
+ computeBoundingBox: function () {
+
+ if ( this.boundingBox === null ) {
+
+ this.boundingBox = new THREE.Box3();
+
+ }
+
+ this.boundingBox.setFromPoints( this.vertices );
+
+ },
+
+ computeBoundingSphere: function () {
+
+ if ( this.boundingSphere === null ) {
+
+ this.boundingSphere = new THREE.Sphere();
+
+ }
+
+ this.boundingSphere.setFromPoints( this.vertices );
+
+ },
+
+ merge: function ( geometry, matrix, materialIndexOffset ) {
+
+ if ( geometry instanceof THREE.Geometry === false ) {
+
+ console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry );
+ return;
+
+ }
+
+ var normalMatrix,
+ vertexOffset = this.vertices.length,
+ vertices1 = this.vertices,
+ vertices2 = geometry.vertices,
+ faces1 = this.faces,
+ faces2 = geometry.faces,
+ uvs1 = this.faceVertexUvs[ 0 ],
+ uvs2 = geometry.faceVertexUvs[ 0 ];
+
+ if ( materialIndexOffset === undefined ) materialIndexOffset = 0;
+
+ if ( matrix !== undefined ) {
+
+ normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );
+
+ }
+
+ // vertices
+
+ for ( var i = 0, il = vertices2.length; i < il; i ++ ) {
+
+ var vertex = vertices2[ i ];
+
+ var vertexCopy = vertex.clone();
+
+ if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix );
+
+ vertices1.push( vertexCopy );
+
+ }
+
+ // faces
+
+ for ( i = 0, il = faces2.length; i < il; i ++ ) {
+
+ var face = faces2[ i ], faceCopy, normal, color,
+ faceVertexNormals = face.vertexNormals,
+ faceVertexColors = face.vertexColors;
+
+ faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset );
+ faceCopy.normal.copy( face.normal );
+
+ if ( normalMatrix !== undefined ) {
+
+ faceCopy.normal.applyMatrix3( normalMatrix ).normalize();
+
+ }
+
+ for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) {
+
+ normal = faceVertexNormals[ j ].clone();
+
+ if ( normalMatrix !== undefined ) {
+
+ normal.applyMatrix3( normalMatrix ).normalize();
+
+ }
+
+ faceCopy.vertexNormals.push( normal );
+
+ }
+
+ faceCopy.color.copy( face.color );
+
+ for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) {
+
+ color = faceVertexColors[ j ];
+ faceCopy.vertexColors.push( color.clone() );
+
+ }
+
+ faceCopy.materialIndex = face.materialIndex + materialIndexOffset;
+
+ faces1.push( faceCopy );
+
+ }
+
+ // uvs
+
+ for ( i = 0, il = uvs2.length; i < il; i ++ ) {
+
+ var uv = uvs2[ i ], uvCopy = [];
+
+ if ( uv === undefined ) {
+
+ continue;
+
+ }
+
+ for ( var j = 0, jl = uv.length; j < jl; j ++ ) {
+
+ uvCopy.push( uv[ j ].clone() );
+
+ }
+
+ uvs1.push( uvCopy );
+
+ }
+
+ },
+
+ mergeMesh: function ( mesh ) {
+
+ if ( mesh instanceof THREE.Mesh === false ) {
+
+ console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh );
+ return;
+
+ }
+
+ mesh.matrixAutoUpdate && mesh.updateMatrix();
+
+ this.merge( mesh.geometry, mesh.matrix );
+
+ },
+
+ /*
+ * Checks for duplicate vertices with hashmap.
+ * Duplicated vertices are removed
+ * and faces' vertices are updated.
+ */
+
+ mergeVertices: function () {
+
+ var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)
+ var unique = [], changes = [];
+
+ var v, key;
+ var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001
+ var precision = Math.pow( 10, precisionPoints );
+ var i, il, face;
+ var indices, j, jl;
+
+ for ( i = 0, il = this.vertices.length; i < il; i ++ ) {
+
+ v = this.vertices[ i ];
+ key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision );
+
+ if ( verticesMap[ key ] === undefined ) {
+
+ verticesMap[ key ] = i;
+ unique.push( this.vertices[ i ] );
+ changes[ i ] = unique.length - 1;
+
+ } else {
+
+ //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]);
+ changes[ i ] = changes[ verticesMap[ key ] ];
+
+ }
+
+ }
+
+
+ // if faces are completely degenerate after merging vertices, we
+ // have to remove them from the geometry.
+ var faceIndicesToRemove = [];
+
+ for ( i = 0, il = this.faces.length; i < il; i ++ ) {
+
+ face = this.faces[ i ];
+
+ face.a = changes[ face.a ];
+ face.b = changes[ face.b ];
+ face.c = changes[ face.c ];
+
+ indices = [ face.a, face.b, face.c ];
+
+ var dupIndex = - 1;
+
+ // if any duplicate vertices are found in a Face3
+ // we have to remove the face as nothing can be saved
+ for ( var n = 0; n < 3; n ++ ) {
+
+ if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) {
+
+ dupIndex = n;
+ faceIndicesToRemove.push( i );
+ break;
+
+ }
+
+ }
+
+ }
+
+ for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) {
+
+ var idx = faceIndicesToRemove[ i ];
+
+ this.faces.splice( idx, 1 );
+
+ for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {
+
+ this.faceVertexUvs[ j ].splice( idx, 1 );
+
+ }
+
+ }
+
+ // Use unique set of vertices
+
+ var diff = this.vertices.length - unique.length;
+ this.vertices = unique;
+ return diff;
+
+ },
+
+ sortFacesByMaterialIndex: function () {
+
+ var faces = this.faces;
+ var length = faces.length;
+
+ // tag faces
+
+ for ( var i = 0; i < length; i ++ ) {
+
+ faces[ i ]._id = i;
+
+ }
+
+ // sort faces
+
+ function materialIndexSort( a, b ) {
+
+ return a.materialIndex - b.materialIndex;
+
+ }
+
+ faces.sort( materialIndexSort );
+
+ // sort uvs
+
+ var uvs1 = this.faceVertexUvs[ 0 ];
+ var uvs2 = this.faceVertexUvs[ 1 ];
+
+ var newUvs1, newUvs2;
+
+ if ( uvs1 && uvs1.length === length ) newUvs1 = [];
+ if ( uvs2 && uvs2.length === length ) newUvs2 = [];
+
+ for ( var i = 0; i < length; i ++ ) {
+
+ var id = faces[ i ]._id;
+
+ if ( newUvs1 ) newUvs1.push( uvs1[ id ] );
+ if ( newUvs2 ) newUvs2.push( uvs2[ id ] );
+
+ }
+
+ if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1;
+ if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2;
+
+ },
+
+ toJSON: function () {
+
+ var data = {
+ metadata: {
+ version: 4.4,
+ type: 'Geometry',
+ generator: 'Geometry.toJSON'
+ }
+ };
+
+ // standard Geometry serialization
+
+ data.uuid = this.uuid;
+ data.type = this.type;
+ if ( this.name !== '' ) data.name = this.name;
+
+ if ( this.parameters !== undefined ) {
+
+ var parameters = this.parameters;
+
+ for ( var key in parameters ) {
+
+ if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];
+
+ }
+
+ return data;
+
+ }
+
+ var vertices = [];
+
+ for ( var i = 0; i < this.vertices.length; i ++ ) {
+
+ var vertex = this.vertices[ i ];
+ vertices.push( vertex.x, vertex.y, vertex.z );
+
+ }
+
+ var faces = [];
+ var normals = [];
+ var normalsHash = {};
+ var colors = [];
+ var colorsHash = {};
+ var uvs = [];
+ var uvsHash = {};
+
+ for ( var i = 0; i < this.faces.length; i ++ ) {
+
+ var face = this.faces[ i ];
+
+ var hasMaterial = false; // face.materialIndex !== undefined;
+ var hasFaceUv = false; // deprecated
+ var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined;
+ var hasFaceNormal = face.normal.length() > 0;
+ var hasFaceVertexNormal = face.vertexNormals.length > 0;
+ var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1;
+ var hasFaceVertexColor = face.vertexColors.length > 0;
+
+ var faceType = 0;
+
+ faceType = setBit( faceType, 0, 0 );
+ faceType = setBit( faceType, 1, hasMaterial );
+ faceType = setBit( faceType, 2, hasFaceUv );
+ faceType = setBit( faceType, 3, hasFaceVertexUv );
+ faceType = setBit( faceType, 4, hasFaceNormal );
+ faceType = setBit( faceType, 5, hasFaceVertexNormal );
+ faceType = setBit( faceType, 6, hasFaceColor );
+ faceType = setBit( faceType, 7, hasFaceVertexColor );
+
+ faces.push( faceType );
+ faces.push( face.a, face.b, face.c );
+
+ if ( hasFaceVertexUv ) {
+
+ var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ];
+
+ faces.push(
+ getUvIndex( faceVertexUvs[ 0 ] ),
+ getUvIndex( faceVertexUvs[ 1 ] ),
+ getUvIndex( faceVertexUvs[ 2 ] )
+ );
+
+ }
+
+ if ( hasFaceNormal ) {
+
+ faces.push( getNormalIndex( face.normal ) );
+
+ }
+
+ if ( hasFaceVertexNormal ) {
+
+ var vertexNormals = face.vertexNormals;
+
+ faces.push(
+ getNormalIndex( vertexNormals[ 0 ] ),
+ getNormalIndex( vertexNormals[ 1 ] ),
+ getNormalIndex( vertexNormals[ 2 ] )
+ );
+
+ }
+
+ if ( hasFaceColor ) {
+
+ faces.push( getColorIndex( face.color ) );
+
+ }
+
+ if ( hasFaceVertexColor ) {
+
+ var vertexColors = face.vertexColors;
+
+ faces.push(
+ getColorIndex( vertexColors[ 0 ] ),
+ getColorIndex( vertexColors[ 1 ] ),
+ getColorIndex( vertexColors[ 2 ] )
+ );
+
+ }
+
+ }
+
+ function setBit( value, position, enabled ) {
+
+ return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) );
+
+ }
+
+ function getNormalIndex( normal ) {
+
+ var hash = normal.x.toString() + normal.y.toString() + normal.z.toString();
+
+ if ( normalsHash[ hash ] !== undefined ) {
+
+ return normalsHash[ hash ];
+
+ }
+
+ normalsHash[ hash ] = normals.length / 3;
+ normals.push( normal.x, normal.y, normal.z );
+
+ return normalsHash[ hash ];
+
+ }
+
+ function getColorIndex( color ) {
+
+ var hash = color.r.toString() + color.g.toString() + color.b.toString();
+
+ if ( colorsHash[ hash ] !== undefined ) {
+
+ return colorsHash[ hash ];
+
+ }
+
+ colorsHash[ hash ] = colors.length;
+ colors.push( color.getHex() );
+
+ return colorsHash[ hash ];
+
+ }
+
+ function getUvIndex( uv ) {
+
+ var hash = uv.x.toString() + uv.y.toString();
+
+ if ( uvsHash[ hash ] !== undefined ) {
+
+ return uvsHash[ hash ];
+
+ }
+
+ uvsHash[ hash ] = uvs.length / 2;
+ uvs.push( uv.x, uv.y );
+
+ return uvsHash[ hash ];
+
+ }
+
+ data.data = {};
+
+ data.data.vertices = vertices;
+ data.data.normals = normals;
+ if ( colors.length > 0 ) data.data.colors = colors;
+ if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility
+ data.data.faces = faces;
+
+ return data;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.vertices = [];
+ this.faces = [];
+ this.faceVertexUvs = [ [] ];
+
+ var vertices = source.vertices;
+
+ for ( var i = 0, il = vertices.length; i < il; i ++ ) {
+
+ this.vertices.push( vertices[ i ].clone() );
+
+ }
+
+ var faces = source.faces;
+
+ for ( var i = 0, il = faces.length; i < il; i ++ ) {
+
+ this.faces.push( faces[ i ].clone() );
+
+ }
+
+ for ( var i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) {
+
+ var faceVertexUvs = source.faceVertexUvs[ i ];
+
+ if ( this.faceVertexUvs[ i ] === undefined ) {
+
+ this.faceVertexUvs[ i ] = [];
+
+ }
+
+ for ( var j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) {
+
+ var uvs = faceVertexUvs[ j ], uvsCopy = [];
+
+ for ( var k = 0, kl = uvs.length; k < kl; k ++ ) {
+
+ var uv = uvs[ k ];
+
+ uvsCopy.push( uv.clone() );
+
+ }
+
+ this.faceVertexUvs[ i ].push( uvsCopy );
+
+ }
+
+ }
+
+ return this;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.Geometry.prototype );
+
+THREE.GeometryIdCount = 0;
+
+// File:src/core/DirectGeometry.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.DirectGeometry = function () {
+
+ Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } );
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.name = '';
+ this.type = 'DirectGeometry';
+
+ this.indices = [];
+ this.vertices = [];
+ this.normals = [];
+ this.colors = [];
+ this.uvs = [];
+ this.uvs2 = [];
+
+ this.groups = [];
+
+ this.morphTargets = {};
+
+ this.skinWeights = [];
+ this.skinIndices = [];
+
+ // this.lineDistances = [];
+
+ this.boundingBox = null;
+ this.boundingSphere = null;
+
+ // update flags
+
+ this.verticesNeedUpdate = false;
+ this.normalsNeedUpdate = false;
+ this.colorsNeedUpdate = false;
+ this.uvsNeedUpdate = false;
+ this.groupsNeedUpdate = false;
+
+};
+
+THREE.DirectGeometry.prototype = {
+
+ constructor: THREE.DirectGeometry,
+
+ computeBoundingBox: THREE.Geometry.prototype.computeBoundingBox,
+ computeBoundingSphere: THREE.Geometry.prototype.computeBoundingSphere,
+
+ computeFaceNormals: function () {
+
+ console.warn( 'THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.' );
+
+ },
+
+ computeVertexNormals: function () {
+
+ console.warn( 'THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.' );
+
+ },
+
+ computeGroups: function ( geometry ) {
+
+ var group;
+ var groups = [];
+ var materialIndex;
+
+ var faces = geometry.faces;
+
+ for ( var i = 0; i < faces.length; i ++ ) {
+
+ var face = faces[ i ];
+
+ // materials
+
+ if ( face.materialIndex !== materialIndex ) {
+
+ materialIndex = face.materialIndex;
+
+ if ( group !== undefined ) {
+
+ group.count = ( i * 3 ) - group.start;
+ groups.push( group );
+
+ }
+
+ group = {
+ start: i * 3,
+ materialIndex: materialIndex
+ };
+
+ }
+
+ }
+
+ if ( group !== undefined ) {
+
+ group.count = ( i * 3 ) - group.start;
+ groups.push( group );
+
+ }
+
+ this.groups = groups;
+
+ },
+
+ fromGeometry: function ( geometry ) {
+
+ var faces = geometry.faces;
+ var vertices = geometry.vertices;
+ var faceVertexUvs = geometry.faceVertexUvs;
+
+ var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0;
+ var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0;
+
+ // morphs
+
+ var morphTargets = geometry.morphTargets;
+ var morphTargetsLength = morphTargets.length;
+
+ if ( morphTargetsLength > 0 ) {
+
+ var morphTargetsPosition = [];
+
+ for ( var i = 0; i < morphTargetsLength; i ++ ) {
+
+ morphTargetsPosition[ i ] = [];
+
+ }
+
+ this.morphTargets.position = morphTargetsPosition;
+
+ }
+
+ var morphNormals = geometry.morphNormals;
+ var morphNormalsLength = morphNormals.length;
+
+ if ( morphNormalsLength > 0 ) {
+
+ var morphTargetsNormal = [];
+
+ for ( var i = 0; i < morphNormalsLength; i ++ ) {
+
+ morphTargetsNormal[ i ] = [];
+
+ }
+
+ this.morphTargets.normal = morphTargetsNormal;
+
+ }
+
+ // skins
+
+ var skinIndices = geometry.skinIndices;
+ var skinWeights = geometry.skinWeights;
+
+ var hasSkinIndices = skinIndices.length === vertices.length;
+ var hasSkinWeights = skinWeights.length === vertices.length;
+
+ //
+
+ for ( var i = 0; i < faces.length; i ++ ) {
+
+ var face = faces[ i ];
+
+ this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] );
+
+ var vertexNormals = face.vertexNormals;
+
+ if ( vertexNormals.length === 3 ) {
+
+ this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] );
+
+ } else {
+
+ var normal = face.normal;
+
+ this.normals.push( normal, normal, normal );
+
+ }
+
+ var vertexColors = face.vertexColors;
+
+ if ( vertexColors.length === 3 ) {
+
+ this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] );
+
+ } else {
+
+ var color = face.color;
+
+ this.colors.push( color, color, color );
+
+ }
+
+ if ( hasFaceVertexUv === true ) {
+
+ var vertexUvs = faceVertexUvs[ 0 ][ i ];
+
+ if ( vertexUvs !== undefined ) {
+
+ this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );
+
+ } else {
+
+ console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i );
+
+ this.uvs.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() );
+
+ }
+
+ }
+
+ if ( hasFaceVertexUv2 === true ) {
+
+ var vertexUvs = faceVertexUvs[ 1 ][ i ];
+
+ if ( vertexUvs !== undefined ) {
+
+ this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );
+
+ } else {
+
+ console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i );
+
+ this.uvs2.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() );
+
+ }
+
+ }
+
+ // morphs
+
+ for ( var j = 0; j < morphTargetsLength; j ++ ) {
+
+ var morphTarget = morphTargets[ j ].vertices;
+
+ morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] );
+
+ }
+
+ for ( var j = 0; j < morphNormalsLength; j ++ ) {
+
+ var morphNormal = morphNormals[ j ].vertexNormals[ i ];
+
+ morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c );
+
+ }
+
+ // skins
+
+ if ( hasSkinIndices ) {
+
+ this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] );
+
+ }
+
+ if ( hasSkinWeights ) {
+
+ this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] );
+
+ }
+
+ }
+
+ this.computeGroups( geometry );
+
+ this.verticesNeedUpdate = geometry.verticesNeedUpdate;
+ this.normalsNeedUpdate = geometry.normalsNeedUpdate;
+ this.colorsNeedUpdate = geometry.colorsNeedUpdate;
+ this.uvsNeedUpdate = geometry.uvsNeedUpdate;
+ this.groupsNeedUpdate = geometry.groupsNeedUpdate;
+
+ return this;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.DirectGeometry.prototype );
+
+// File:src/core/BufferGeometry.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.BufferGeometry = function () {
+
+ Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } );
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.name = '';
+ this.type = 'BufferGeometry';
+
+ this.index = null;
+ this.attributes = {};
+
+ this.morphAttributes = {};
+
+ this.groups = [];
+
+ this.boundingBox = null;
+ this.boundingSphere = null;
+
+ this.drawRange = { start: 0, count: Infinity };
+
+};
+
+THREE.BufferGeometry.prototype = {
+
+ constructor: THREE.BufferGeometry,
+
+ addIndex: function ( index ) {
+
+ console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' );
+ this.setIndex( index );
+
+ },
+
+ getIndex: function () {
+
+ return this.index;
+
+ },
+
+ setIndex: function ( index ) {
+
+ this.index = index;
+
+ },
+
+ addAttribute: function ( name, attribute ) {
+
+ if ( attribute instanceof THREE.BufferAttribute === false && attribute instanceof THREE.InterleavedBufferAttribute === false ) {
+
+ console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' );
+
+ this.addAttribute( name, new THREE.BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) );
+
+ return;
+
+ }
+
+ if ( name === 'index' ) {
+
+ console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' );
+ this.setIndex( attribute );
+
+ return;
+
+ }
+
+ this.attributes[ name ] = attribute;
+
+ },
+
+ getAttribute: function ( name ) {
+
+ return this.attributes[ name ];
+
+ },
+
+ removeAttribute: function ( name ) {
+
+ delete this.attributes[ name ];
+
+ },
+
+ get drawcalls() {
+
+ console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' );
+ return this.groups;
+
+ },
+
+ get offsets() {
+
+ console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' );
+ return this.groups;
+
+ },
+
+ addDrawCall: function ( start, count, indexOffset ) {
+
+ if ( indexOffset !== undefined ) {
+
+ console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' );
+
+ }
+
+ console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' );
+ this.addGroup( start, count );
+
+ },
+
+ clearDrawCalls: function () {
+
+ console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' );
+ this.clearGroups();
+
+ },
+
+ addGroup: function ( start, count, materialIndex ) {
+
+ this.groups.push( {
+
+ start: start,
+ count: count,
+ materialIndex: materialIndex !== undefined ? materialIndex : 0
+
+ } );
+
+ },
+
+ clearGroups: function () {
+
+ this.groups = [];
+
+ },
+
+ setDrawRange: function ( start, count ) {
+
+ this.drawRange.start = start;
+ this.drawRange.count = count;
+
+ },
+
+ applyMatrix: function ( matrix ) {
+
+ var position = this.attributes.position;
+
+ if ( position !== undefined ) {
+
+ matrix.applyToVector3Array( position.array );
+ position.needsUpdate = true;
+
+ }
+
+ var normal = this.attributes.normal;
+
+ if ( normal !== undefined ) {
+
+ var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );
+
+ normalMatrix.applyToVector3Array( normal.array );
+ normal.needsUpdate = true;
+
+ }
+
+ if ( this.boundingBox !== null ) {
+
+ this.computeBoundingBox();
+
+ }
+
+ if ( this.boundingSphere !== null ) {
+
+ this.computeBoundingSphere();
+
+ }
+
+ },
+
+ rotateX: function () {
+
+ // rotate geometry around world x-axis
+
+ var m1;
+
+ return function rotateX( angle ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeRotationX( angle );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ rotateY: function () {
+
+ // rotate geometry around world y-axis
+
+ var m1;
+
+ return function rotateY( angle ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeRotationY( angle );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ rotateZ: function () {
+
+ // rotate geometry around world z-axis
+
+ var m1;
+
+ return function rotateZ( angle ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeRotationZ( angle );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ translate: function () {
+
+ // translate geometry
+
+ var m1;
+
+ return function translate( x, y, z ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeTranslation( x, y, z );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ scale: function () {
+
+ // scale geometry
+
+ var m1;
+
+ return function scale( x, y, z ) {
+
+ if ( m1 === undefined ) m1 = new THREE.Matrix4();
+
+ m1.makeScale( x, y, z );
+
+ this.applyMatrix( m1 );
+
+ return this;
+
+ };
+
+ }(),
+
+ lookAt: function () {
+
+ var obj;
+
+ return function lookAt( vector ) {
+
+ if ( obj === undefined ) obj = new THREE.Object3D();
+
+ obj.lookAt( vector );
+
+ obj.updateMatrix();
+
+ this.applyMatrix( obj.matrix );
+
+ };
+
+ }(),
+
+ center: function () {
+
+ this.computeBoundingBox();
+
+ var offset = this.boundingBox.center().negate();
+
+ this.translate( offset.x, offset.y, offset.z );
+
+ return offset;
+
+ },
+
+ setFromObject: function ( object ) {
+
+ // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this );
+
+ var geometry = object.geometry;
+
+ if ( object instanceof THREE.Points || object instanceof THREE.Line ) {
+
+ var positions = new THREE.Float32Attribute( geometry.vertices.length * 3, 3 );
+ var colors = new THREE.Float32Attribute( geometry.colors.length * 3, 3 );
+
+ this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) );
+ this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) );
+
+ if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) {
+
+ var lineDistances = new THREE.Float32Attribute( geometry.lineDistances.length, 1 );
+
+ this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) );
+
+ }
+
+ if ( geometry.boundingSphere !== null ) {
+
+ this.boundingSphere = geometry.boundingSphere.clone();
+
+ }
+
+ if ( geometry.boundingBox !== null ) {
+
+ this.boundingBox = geometry.boundingBox.clone();
+
+ }
+
+ } else if ( object instanceof THREE.Mesh ) {
+
+ if ( geometry instanceof THREE.Geometry ) {
+
+ this.fromGeometry( geometry );
+
+ }
+
+ }
+
+ return this;
+
+ },
+
+ updateFromObject: function ( object ) {
+
+ var geometry = object.geometry;
+
+ if ( object instanceof THREE.Mesh ) {
+
+ var direct = geometry.__directGeometry;
+
+ if ( direct === undefined ) {
+
+ return this.fromGeometry( geometry );
+
+ }
+
+ direct.verticesNeedUpdate = geometry.verticesNeedUpdate;
+ direct.normalsNeedUpdate = geometry.normalsNeedUpdate;
+ direct.colorsNeedUpdate = geometry.colorsNeedUpdate;
+ direct.uvsNeedUpdate = geometry.uvsNeedUpdate;
+ direct.groupsNeedUpdate = geometry.groupsNeedUpdate;
+
+ geometry.verticesNeedUpdate = false;
+ geometry.normalsNeedUpdate = false;
+ geometry.colorsNeedUpdate = false;
+ geometry.uvsNeedUpdate = false;
+ geometry.groupsNeedUpdate = false;
+
+ geometry = direct;
+
+ }
+
+ if ( geometry.verticesNeedUpdate === true ) {
+
+ var attribute = this.attributes.position;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyVector3sArray( geometry.vertices );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.verticesNeedUpdate = false;
+
+ }
+
+ if ( geometry.normalsNeedUpdate === true ) {
+
+ var attribute = this.attributes.normal;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyVector3sArray( geometry.normals );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.normalsNeedUpdate = false;
+
+ }
+
+ if ( geometry.colorsNeedUpdate === true ) {
+
+ var attribute = this.attributes.color;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyColorsArray( geometry.colors );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.colorsNeedUpdate = false;
+
+ }
+
+ if ( geometry.uvsNeedUpdate ) {
+
+ var attribute = this.attributes.uv;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyVector2sArray( geometry.uvs );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.uvsNeedUpdate = false;
+
+ }
+
+ if ( geometry.lineDistancesNeedUpdate ) {
+
+ var attribute = this.attributes.lineDistance;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyArray( geometry.lineDistances );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.lineDistancesNeedUpdate = false;
+
+ }
+
+ if ( geometry.groupsNeedUpdate ) {
+
+ geometry.computeGroups( object.geometry );
+ this.groups = geometry.groups;
+
+ geometry.groupsNeedUpdate = false;
+
+ }
+
+ return this;
+
+ },
+
+ fromGeometry: function ( geometry ) {
+
+ geometry.__directGeometry = new THREE.DirectGeometry().fromGeometry( geometry );
+
+ return this.fromDirectGeometry( geometry.__directGeometry );
+
+ },
+
+ fromDirectGeometry: function ( geometry ) {
+
+ var positions = new Float32Array( geometry.vertices.length * 3 );
+ this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) );
+
+ if ( geometry.normals.length > 0 ) {
+
+ var normals = new Float32Array( geometry.normals.length * 3 );
+ this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) );
+
+ }
+
+ if ( geometry.colors.length > 0 ) {
+
+ var colors = new Float32Array( geometry.colors.length * 3 );
+ this.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) );
+
+ }
+
+ if ( geometry.uvs.length > 0 ) {
+
+ var uvs = new Float32Array( geometry.uvs.length * 2 );
+ this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) );
+
+ }
+
+ if ( geometry.uvs2.length > 0 ) {
+
+ var uvs2 = new Float32Array( geometry.uvs2.length * 2 );
+ this.addAttribute( 'uv2', new THREE.BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) );
+
+ }
+
+ if ( geometry.indices.length > 0 ) {
+
+ var TypeArray = geometry.vertices.length > 65535 ? Uint32Array : Uint16Array;
+ var indices = new TypeArray( geometry.indices.length * 3 );
+ this.setIndex( new THREE.BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices ) );
+
+ }
+
+ // groups
+
+ this.groups = geometry.groups;
+
+ // morphs
+
+ for ( var name in geometry.morphTargets ) {
+
+ var array = [];
+ var morphTargets = geometry.morphTargets[ name ];
+
+ for ( var i = 0, l = morphTargets.length; i < l; i ++ ) {
+
+ var morphTarget = morphTargets[ i ];
+
+ var attribute = new THREE.Float32Attribute( morphTarget.length * 3, 3 );
+
+ array.push( attribute.copyVector3sArray( morphTarget ) );
+
+ }
+
+ this.morphAttributes[ name ] = array;
+
+ }
+
+ // skinning
+
+ if ( geometry.skinIndices.length > 0 ) {
+
+ var skinIndices = new THREE.Float32Attribute( geometry.skinIndices.length * 4, 4 );
+ this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) );
+
+ }
+
+ if ( geometry.skinWeights.length > 0 ) {
+
+ var skinWeights = new THREE.Float32Attribute( geometry.skinWeights.length * 4, 4 );
+ this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) );
+
+ }
+
+ //
+
+ if ( geometry.boundingSphere !== null ) {
+
+ this.boundingSphere = geometry.boundingSphere.clone();
+
+ }
+
+ if ( geometry.boundingBox !== null ) {
+
+ this.boundingBox = geometry.boundingBox.clone();
+
+ }
+
+ return this;
+
+ },
+
+ computeBoundingBox: function () {
+
+ var vector = new THREE.Vector3();
+
+ return function () {
+
+ if ( this.boundingBox === null ) {
+
+ this.boundingBox = new THREE.Box3();
+
+ }
+
+ var positions = this.attributes.position.array;
+
+ if ( positions ) {
+
+ var bb = this.boundingBox;
+ bb.makeEmpty();
+
+ for ( var i = 0, il = positions.length; i < il; i += 3 ) {
+
+ vector.fromArray( positions, i );
+ bb.expandByPoint( vector );
+
+ }
+
+ }
+
+ if ( positions === undefined || positions.length === 0 ) {
+
+ this.boundingBox.min.set( 0, 0, 0 );
+ this.boundingBox.max.set( 0, 0, 0 );
+
+ }
+
+ if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {
+
+ console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this );
+
+ }
+
+ };
+
+ }(),
+
+ computeBoundingSphere: function () {
+
+ var box = new THREE.Box3();
+ var vector = new THREE.Vector3();
+
+ return function () {
+
+ if ( this.boundingSphere === null ) {
+
+ this.boundingSphere = new THREE.Sphere();
+
+ }
+
+ var positions = this.attributes.position.array;
+
+ if ( positions ) {
+
+ box.makeEmpty();
+
+ var center = this.boundingSphere.center;
+
+ for ( var i = 0, il = positions.length; i < il; i += 3 ) {
+
+ vector.fromArray( positions, i );
+ box.expandByPoint( vector );
+
+ }
+
+ box.center( center );
+
+ // hoping to find a boundingSphere with a radius smaller than the
+ // boundingSphere of the boundingBox: sqrt(3) smaller in the best case
+
+ var maxRadiusSq = 0;
+
+ for ( var i = 0, il = positions.length; i < il; i += 3 ) {
+
+ vector.fromArray( positions, i );
+ maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) );
+
+ }
+
+ this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
+
+ if ( isNaN( this.boundingSphere.radius ) ) {
+
+ console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this );
+
+ }
+
+ }
+
+ };
+
+ }(),
+
+ computeFaceNormals: function () {
+
+ // backwards compatibility
+
+ },
+
+ computeVertexNormals: function () {
+
+ var index = this.index;
+ var attributes = this.attributes;
+ var groups = this.groups;
+
+ if ( attributes.position ) {
+
+ var positions = attributes.position.array;
+
+ if ( attributes.normal === undefined ) {
+
+ this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( positions.length ), 3 ) );
+
+ } else {
+
+ // reset existing normals to zero
+
+ var normals = attributes.normal.array;
+
+ for ( var i = 0, il = normals.length; i < il; i ++ ) {
+
+ normals[ i ] = 0;
+
+ }
+
+ }
+
+ var normals = attributes.normal.array;
+
+ var vA, vB, vC,
+
+ pA = new THREE.Vector3(),
+ pB = new THREE.Vector3(),
+ pC = new THREE.Vector3(),
+
+ cb = new THREE.Vector3(),
+ ab = new THREE.Vector3();
+
+ // indexed elements
+
+ if ( index ) {
+
+ var indices = index.array;
+
+ if ( groups.length === 0 ) {
+
+ this.addGroup( 0, indices.length );
+
+ }
+
+ for ( var j = 0, jl = groups.length; j < jl; ++ j ) {
+
+ var group = groups[ j ];
+
+ var start = group.start;
+ var count = group.count;
+
+ for ( var i = start, il = start + count; i < il; i += 3 ) {
+
+ vA = indices[ i + 0 ] * 3;
+ vB = indices[ i + 1 ] * 3;
+ vC = indices[ i + 2 ] * 3;
+
+ pA.fromArray( positions, vA );
+ pB.fromArray( positions, vB );
+ pC.fromArray( positions, vC );
+
+ cb.subVectors( pC, pB );
+ ab.subVectors( pA, pB );
+ cb.cross( ab );
+
+ normals[ vA ] += cb.x;
+ normals[ vA + 1 ] += cb.y;
+ normals[ vA + 2 ] += cb.z;
+
+ normals[ vB ] += cb.x;
+ normals[ vB + 1 ] += cb.y;
+ normals[ vB + 2 ] += cb.z;
+
+ normals[ vC ] += cb.x;
+ normals[ vC + 1 ] += cb.y;
+ normals[ vC + 2 ] += cb.z;
+
+ }
+
+ }
+
+ } else {
+
+ // non-indexed elements (unconnected triangle soup)
+
+ for ( var i = 0, il = positions.length; i < il; i += 9 ) {
+
+ pA.fromArray( positions, i );
+ pB.fromArray( positions, i + 3 );
+ pC.fromArray( positions, i + 6 );
+
+ cb.subVectors( pC, pB );
+ ab.subVectors( pA, pB );
+ cb.cross( ab );
+
+ normals[ i ] = cb.x;
+ normals[ i + 1 ] = cb.y;
+ normals[ i + 2 ] = cb.z;
+
+ normals[ i + 3 ] = cb.x;
+ normals[ i + 4 ] = cb.y;
+ normals[ i + 5 ] = cb.z;
+
+ normals[ i + 6 ] = cb.x;
+ normals[ i + 7 ] = cb.y;
+ normals[ i + 8 ] = cb.z;
+
+ }
+
+ }
+
+ this.normalizeNormals();
+
+ attributes.normal.needsUpdate = true;
+
+ }
+
+ },
+
+ computeTangents: function () {
+
+ console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' );
+
+ },
+
+ computeOffsets: function ( size ) {
+
+ console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.')
+
+ },
+
+ merge: function ( geometry, offset ) {
+
+ if ( geometry instanceof THREE.BufferGeometry === false ) {
+
+ console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry );
+ return;
+
+ }
+
+ if ( offset === undefined ) offset = 0;
+
+ var attributes = this.attributes;
+
+ for ( var key in attributes ) {
+
+ if ( geometry.attributes[ key ] === undefined ) continue;
+
+ var attribute1 = attributes[ key ];
+ var attributeArray1 = attribute1.array;
+
+ var attribute2 = geometry.attributes[ key ];
+ var attributeArray2 = attribute2.array;
+
+ var attributeSize = attribute2.itemSize;
+
+ for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) {
+
+ attributeArray1[ j ] = attributeArray2[ i ];
+
+ }
+
+ }
+
+ return this;
+
+ },
+
+ normalizeNormals: function () {
+
+ var normals = this.attributes.normal.array;
+
+ var x, y, z, n;
+
+ for ( var i = 0, il = normals.length; i < il; i += 3 ) {
+
+ x = normals[ i ];
+ y = normals[ i + 1 ];
+ z = normals[ i + 2 ];
+
+ n = 1.0 / Math.sqrt( x * x + y * y + z * z );
+
+ normals[ i ] *= n;
+ normals[ i + 1 ] *= n;
+ normals[ i + 2 ] *= n;
+
+ }
+
+ },
+
+ toJSON: function () {
+
+ var data = {
+ metadata: {
+ version: 4.4,
+ type: 'BufferGeometry',
+ generator: 'BufferGeometry.toJSON'
+ }
+ };
+
+ // standard BufferGeometry serialization
+
+ data.uuid = this.uuid;
+ data.type = this.type;
+ if ( this.name !== '' ) data.name = this.name;
+
+ if ( this.parameters !== undefined ) {
+
+ var parameters = this.parameters;
+
+ for ( var key in parameters ) {
+
+ if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];
+
+ }
+
+ return data;
+
+ }
+
+ data.data = { attributes: {} };
+
+ var index = this.index;
+
+ if ( index !== null ) {
+
+ var array = Array.prototype.slice.call( index.array );
+
+ data.data.index = {
+ type: index.array.constructor.name,
+ array: array
+ };
+
+ }
+
+ var attributes = this.attributes;
+
+ for ( var key in attributes ) {
+
+ var attribute = attributes[ key ];
+
+ var array = Array.prototype.slice.call( attribute.array );
+
+ data.data.attributes[ key ] = {
+ itemSize: attribute.itemSize,
+ type: attribute.array.constructor.name,
+ array: array
+ };
+
+ }
+
+ var groups = this.groups;
+
+ if ( groups.length > 0 ) {
+
+ data.data.groups = JSON.parse( JSON.stringify( groups ) );
+
+ }
+
+ var boundingSphere = this.boundingSphere;
+
+ if ( boundingSphere !== null ) {
+
+ data.data.boundingSphere = {
+ center: boundingSphere.center.toArray(),
+ radius: boundingSphere.radius
+ };
+
+ }
+
+ return data;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ var index = source.index;
+
+ if ( index !== null ) {
+
+ this.setIndex( index.clone() );
+
+ }
+
+ var attributes = source.attributes;
+
+ for ( var name in attributes ) {
+
+ var attribute = attributes[ name ];
+ this.addAttribute( name, attribute.clone() );
+
+ }
+
+ var groups = source.groups;
+
+ for ( var i = 0, l = groups.length; i < l; i ++ ) {
+
+ var group = groups[ i ];
+ this.addGroup( group.start, group.count );
+
+ }
+
+ return this;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype );
+
+THREE.BufferGeometry.MaxIndex = 65535;
+
+// File:src/core/InstancedBufferGeometry.js
+
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InstancedBufferGeometry = function () {
+
+ THREE.BufferGeometry.call( this );
+
+ this.type = 'InstancedBufferGeometry';
+ this.maxInstancedCount = undefined;
+
+};
+
+THREE.InstancedBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.InstancedBufferGeometry.prototype.constructor = THREE.InstancedBufferGeometry;
+
+THREE.InstancedBufferGeometry.prototype.addGroup = function ( start, count, instances ) {
+
+ this.groups.push( {
+
+ start: start,
+ count: count,
+ instances: instances
+
+ } );
+
+};
+
+THREE.InstancedBufferGeometry.prototype.copy = function ( source ) {
+
+ var index = source.index;
+
+ if ( index !== null ) {
+
+ this.setIndex( index.clone() );
+
+ }
+
+ var attributes = source.attributes;
+
+ for ( var name in attributes ) {
+
+ var attribute = attributes[ name ];
+ this.addAttribute( name, attribute.clone() );
+
+ }
+
+ var groups = source.groups;
+
+ for ( var i = 0, l = groups.length; i < l; i ++ ) {
+
+ var group = groups[ i ];
+ this.addGroup( group.start, group.count, group.instances );
+
+ }
+
+ return this;
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.InstancedBufferGeometry.prototype );
+
+// File:src/animation/AnimationAction.js
+
+/**
+ *
+ * A clip that has been explicitly scheduled.
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.AnimationAction = function ( clip, startTime, timeScale, weight, loop ) {
+
+ if ( clip === undefined ) throw new Error( 'clip is null' );
+ this.clip = clip;
+ this.localRoot = null;
+ this.startTime = startTime || 0;
+ this.timeScale = timeScale || 1;
+ this.weight = weight || 1;
+ this.loop = loop || THREE.LoopRepeat;
+ this.loopCount = 0;
+ this.enabled = true; // allow for easy disabling of the action.
+
+ this.actionTime = - this.startTime;
+ this.clipTime = 0;
+
+ this.propertyBindings = [];
+};
+
+/*
+THREE.LoopOnce = 2200;
+THREE.LoopRepeat = 2201;
+THREE.LoopPingPing = 2202;
+*/
+
+THREE.AnimationAction.prototype = {
+
+ constructor: THREE.AnimationAction,
+
+ setLocalRoot: function( localRoot ) {
+
+ this.localRoot = localRoot;
+
+ return this;
+
+ },
+
+ updateTime: function( clipDeltaTime ) {
+
+ var previousClipTime = this.clipTime;
+ var previousLoopCount = this.loopCount;
+ var previousActionTime = this.actionTime;
+
+ var duration = this.clip.duration;
+
+ this.actionTime = this.actionTime + clipDeltaTime;
+
+ if ( this.loop === THREE.LoopOnce ) {
+
+ this.loopCount = 0;
+ this.clipTime = Math.min( Math.max( this.actionTime, 0 ), duration );
+
+ // if time is changed since last time, see if we have hit a start/end limit
+ if ( this.clipTime !== previousClipTime ) {
+
+ if ( this.clipTime === duration ) {
+
+ this.mixer.dispatchEvent( { type: 'finished', action: this, direction: 1 } );
+
+ } else if ( this.clipTime === 0 ) {
+
+ this.mixer.dispatchEvent( { type: 'finished', action: this, direction: -1 } );
+
+ }
+
+ }
+
+
+ return this.clipTime;
+
+ }
+
+ this.loopCount = Math.floor( this.actionTime / duration );
+
+ var newClipTime = this.actionTime - this.loopCount * duration;
+ newClipTime = newClipTime % duration;
+
+ // if we are ping pong looping, ensure that we go backwards when appropriate
+ if ( this.loop == THREE.LoopPingPong ) {
+
+ if ( Math.abs( this.loopCount % 2 ) === 1 ) {
+
+ newClipTime = duration - newClipTime;
+
+ }
+
+ }
+
+ this.clipTime = newClipTime;
+
+ if ( this.loopCount !== previousLoopCount ) {
+
+ this.mixer.dispatchEvent( { type: 'loop', action: this, loopDelta: ( this.loopCount - this.loopCount ) } );
+
+ }
+
+ return this.clipTime;
+
+ },
+
+ syncWith: function( action ) {
+
+ this.actionTime = action.actionTime;
+ this.timeScale = action.timeScale;
+
+ return this;
+ },
+
+ warpToDuration: function( duration ) {
+
+ this.timeScale = this.clip.duration / duration;
+
+ return this;
+ },
+
+ init: function( time ) {
+
+ this.clipTime = time - this.startTime;
+
+ return this;
+
+ },
+
+ update: function( clipDeltaTime ) {
+
+ this.updateTime( clipDeltaTime );
+
+ var clipResults = this.clip.getAt( this.clipTime );
+
+ return clipResults;
+
+ },
+
+ getTimeScaleAt: function( time ) {
+
+ if ( this.timeScale.getAt ) {
+ // pass in time, not clip time, allows for fadein/fadeout across multiple loops of the clip
+ return this.timeScale.getAt( time );
+
+ }
+
+ return this.timeScale;
+
+ },
+
+ getWeightAt: function( time ) {
+
+ if ( this.weight.getAt ) {
+ // pass in time, not clip time, allows for fadein/fadeout across multiple loops of the clip
+ return this.weight.getAt( time );
+
+ }
+
+ return this.weight;
+
+ }
+
+};
+
+// File:src/animation/AnimationClip.js
+
+/**
+ *
+ * Reusable set of Tracks that represent an animation.
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.AnimationClip = function ( name, duration, tracks ) {
+
+ this.name = name;
+ this.tracks = tracks;
+ this.duration = ( duration !== undefined ) ? duration : -1;
+
+ // this means it should figure out its duration by scanning the tracks
+ if ( this.duration < 0 ) {
+ for ( var i = 0; i < this.tracks.length; i ++ ) {
+ var track = this.tracks[i];
+ this.duration = Math.max( track.keys[ track.keys.length - 1 ].time );
+ }
+ }
+
+ // maybe only do these on demand, as doing them here could potentially slow down loading
+ // but leaving these here during development as this ensures a lot of testing of these functions
+ this.trim();
+ this.optimize();
+
+ this.results = [];
+
+};
+
+THREE.AnimationClip.prototype = {
+
+ constructor: THREE.AnimationClip,
+
+ getAt: function( clipTime ) {
+
+ clipTime = Math.max( 0, Math.min( clipTime, this.duration ) );
+
+ for ( var i = 0; i < this.tracks.length; i ++ ) {
+
+ var track = this.tracks[ i ];
+
+ this.results[ i ] = track.getAt( clipTime );
+
+ }
+
+ return this.results;
+ },
+
+ trim: function() {
+
+ for ( var i = 0; i < this.tracks.length; i ++ ) {
+
+ this.tracks[ i ].trim( 0, this.duration );
+
+ }
+
+ return this;
+
+ },
+
+ optimize: function() {
+
+ for ( var i = 0; i < this.tracks.length; i ++ ) {
+
+ this.tracks[ i ].optimize();
+
+ }
+
+ return this;
+
+ }
+
+};
+
+
+THREE.AnimationClip.CreateFromMorphTargetSequence = function( name, morphTargetSequence, fps ) {
+
+
+ var numMorphTargets = morphTargetSequence.length;
+ var tracks = [];
+
+ for ( var i = 0; i < numMorphTargets; i ++ ) {
+
+ var keys = [];
+
+ keys.push( { time: ( i + numMorphTargets - 1 ) % numMorphTargets, value: 0 } );
+ keys.push( { time: i, value: 1 } );
+ keys.push( { time: ( i + 1 ) % numMorphTargets, value: 0 } );
+
+ keys.sort( THREE.KeyframeTrack.keyComparer );
+
+ // if there is a key at the first frame, duplicate it as the last frame as well for perfect loop.
+ if ( keys[0].time === 0 ) {
+ keys.push( {
+ time: numMorphTargets,
+ value: keys[0].value
+ });
+ }
+
+ tracks.push( new THREE.NumberKeyframeTrack( '.morphTargetInfluences[' + morphTargetSequence[i].name + ']', keys ).scale( 1.0 / fps ) );
+ }
+
+ return new THREE.AnimationClip( name, -1, tracks );
+
+};
+
+THREE.AnimationClip.findByName = function( clipArray, name ) {
+
+ for ( var i = 0; i < clipArray.length; i ++ ) {
+
+ if ( clipArray[i].name === name ) {
+
+ return clipArray[i];
+
+ }
+ }
+
+ return null;
+
+};
+
+THREE.AnimationClip.CreateClipsFromMorphTargetSequences = function( morphTargets, fps ) {
+
+ var animationToMorphTargets = {};
+
+ // tested with https://regex101.com/ on trick sequences such flamingo_flyA_003, flamingo_run1_003, crdeath0059
+ var pattern = /^([\w-]*?)([\d]+)$/;
+
+ // sort morph target names into animation groups based patterns like Walk_001, Walk_002, Run_001, Run_002
+ for ( var i = 0, il = morphTargets.length; i < il; i ++ ) {
+
+ var morphTarget = morphTargets[ i ];
+ var parts = morphTarget.name.match( pattern );
+
+ if ( parts && parts.length > 1 ) {
+
+ var name = parts[ 1 ];
+
+ var animationMorphTargets = animationToMorphTargets[ name ];
+ if ( ! animationMorphTargets ) {
+ animationToMorphTargets[ name ] = animationMorphTargets = [];
+ }
+
+ animationMorphTargets.push( morphTarget );
+
+ }
+
+ }
+
+ var clips = [];
+
+ for ( var name in animationToMorphTargets ) {
+
+ clips.push( THREE.AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps ) );
+ }
+
+ return clips;
+
+};
+
+// parse the standard JSON format for clips
+THREE.AnimationClip.parse = function( json ) {
+
+ var tracks = [];
+
+ for ( var i = 0; i < json.tracks.length; i ++ ) {
+
+ tracks.push( THREE.KeyframeTrack.parse( json.tracks[i] ).scale( 1.0 / json.fps ) );
+
+ }
+
+ return new THREE.AnimationClip( json.name, json.duration, tracks );
+
+};
+
+
+// parse the animation.hierarchy format
+THREE.AnimationClip.parseAnimation = function( animation, bones, nodeName ) {
+
+ if ( ! animation ) {
+ console.error( " no animation in JSONLoader data" );
+ return null;
+ }
+
+ var convertTrack = function( trackName, animationKeys, propertyName, trackType, animationKeyToValueFunc ) {
+
+ var keys = [];
+
+ for ( var k = 0; k < animationKeys.length; k ++ ) {
+
+ var animationKey = animationKeys[k];
+
+ if ( animationKey[propertyName] !== undefined ) {
+
+ keys.push( { time: animationKey.time, value: animationKeyToValueFunc( animationKey ) } );
+ }
+
+ }
+
+ // only return track if there are actually keys.
+ if ( keys.length > 0 ) {
+
+ return new trackType( trackName, keys );
+
+ }
+
+ return null;
+
+ };
+
+ var tracks = [];
+
+ var clipName = animation.name || 'default';
+ var duration = animation.length || -1; // automatic length determination in AnimationClip.
+ var fps = animation.fps || 30;
+
+ var hierarchyTracks = animation.hierarchy || [];
+
+ for ( var h = 0; h < hierarchyTracks.length; h ++ ) {
+
+ var animationKeys = hierarchyTracks[ h ].keys;
+
+ // skip empty tracks
+ if ( ! animationKeys || animationKeys.length == 0 ) {
+ continue;
+ }
+
+ // process morph targets in a way exactly compatible with AnimationHandler.init( animation )
+ if ( animationKeys[0].morphTargets ) {
+
+ // figure out all morph targets used in this track
+ var morphTargetNames = {};
+ for ( var k = 0; k < animationKeys.length; k ++ ) {
+
+ if ( animationKeys[k].morphTargets ) {
+ for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) {
+
+ morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1;
+ }
+ }
+
+ }
+
+ // create a track for each morph target with all zero morphTargetInfluences except for the keys in which the morphTarget is named.
+ for ( var morphTargetName in morphTargetNames ) {
+
+ var keys = [];
+
+ for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) {
+
+ var animationKey = animationKeys[k];
+
+ keys.push( {
+ time: animationKey.time,
+ value: (( animationKey.morphTarget === morphTargetName ) ? 1 : 0 )
+ });
+
+ }
+
+ tracks.push( new THREE.NumberKeyframeTrack( nodeName + '.morphTargetInfluence[' + morphTargetName + ']', keys ) );
+
+ }
+
+ duration = morphTargetNames.length * ( fps || 1.0 );
+
+ } else {
+
+ var boneName = nodeName + '.bones[' + bones[ h ].name + ']';
+
+ // track contains positions...
+ var positionTrack = convertTrack( boneName + '.position', animationKeys, 'pos', THREE.VectorKeyframeTrack, function( animationKey ) {
+ return new THREE.Vector3().fromArray( animationKey.pos )
+ } );
+
+ if ( positionTrack ) tracks.push( positionTrack );
+
+ // track contains quaternions...
+ var quaternionTrack = convertTrack( boneName + '.quaternion', animationKeys, 'rot', THREE.QuaternionKeyframeTrack, function( animationKey ) {
+ if ( animationKey.rot.slerp ) {
+ return animationKey.rot.clone();
+ } else {
+ return new THREE.Quaternion().fromArray( animationKey.rot );
+ }
+ } );
+
+ if ( quaternionTrack ) tracks.push( quaternionTrack );
+
+ // track contains quaternions...
+ var scaleTrack = convertTrack( boneName + '.scale', animationKeys, 'scl', THREE.VectorKeyframeTrack, function( animationKey ) {
+ return new THREE.Vector3().fromArray( animationKey.scl )
+ } );
+
+ if ( scaleTrack ) tracks.push( scaleTrack );
+
+ }
+ }
+
+ if ( tracks.length === 0 ) {
+
+ return null;
+
+ }
+
+ var clip = new THREE.AnimationClip( clipName, duration, tracks );
+
+ return clip;
+
+};
+
+// File:src/animation/AnimationMixer.js
+
+/**
+ *
+ * Mixes together the AnimationClips scheduled by AnimationActions and applies them to the root and subtree
+ *
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.AnimationMixer = function( root ) {
+
+ this.root = root;
+ this.time = 0;
+ this.timeScale = 1.0;
+ this.actions = [];
+ this.propertyBindingMap = {};
+
+};
+
+THREE.AnimationMixer.prototype = {
+
+ constructor: THREE.AnimationMixer,
+
+ addAction: function( action ) {
+
+ // TODO: check for duplicate action names? Or provide each action with a UUID?
+
+ this.actions.push( action );
+ action.init( this.time );
+ action.mixer = this;
+
+ var tracks = action.clip.tracks;
+
+ var root = action.localRoot || this.root;
+
+ for ( var i = 0; i < tracks.length; i ++ ) {
+
+ var track = tracks[ i ];
+
+ var propertyBindingKey = root.uuid + '-' + track.name;
+ var propertyBinding = this.propertyBindingMap[ propertyBindingKey ];
+
+ if ( propertyBinding === undefined ) {
+
+ propertyBinding = new THREE.PropertyBinding( root, track.name );
+ this.propertyBindingMap[ propertyBindingKey ] = propertyBinding;
+
+ }
+
+ // push in the same order as the tracks.
+ action.propertyBindings.push( propertyBinding );
+
+ // track usages of shared property bindings, because if we leave too many around, the mixer can get slow
+ propertyBinding.referenceCount += 1;
+
+ }
+
+ },
+
+ removeAllActions: function() {
+
+ for ( var i = 0; i < this.actions.length; i ++ ) {
+
+ this.actions[i].mixer = null;
+
+ }
+
+ // unbind all property bindings
+ for ( var properyBindingKey in this.propertyBindingMap ) {
+
+ this.propertyBindingMap[ properyBindingKey ].unbind();
+
+ }
+
+ this.actions = [];
+ this.propertyBindingMap = {};
+
+ return this;
+
+ },
+
+ removeAction: function( action ) {
+
+ var index = this.actions.indexOf( action );
+
+ if ( index !== - 1 ) {
+
+ this.actions.splice( index, 1 );
+ action.mixer = null;
+
+ }
+
+
+ // remove unused property bindings because if we leave them around the mixer can get slow
+ var root = action.localRoot || this.root;
+ var tracks = action.clip.tracks;
+
+ for ( var i = 0; i < tracks.length; i ++ ) {
+
+ var track = tracks[ i ];
+
+ var propertyBindingKey = root.uuid + '-' + track.name;
+ var propertyBinding = this.propertyBindingMap[ propertyBindingKey ];
+
+ propertyBinding.referenceCount -= 1;
+
+ if ( propertyBinding.referenceCount <= 0 ) {
+
+ propertyBinding.unbind();
+
+ delete this.propertyBindingMap[ propertyBindingKey ];
+
+ }
+ }
+
+ return this;
+
+ },
+
+ // can be optimized if needed
+ findActionByName: function( name ) {
+
+ for ( var i = 0; i < this.actions.length; i ++ ) {
+
+ if ( this.actions[i].name === name ) return this.actions[i];
+
+ }
+
+ return null;
+
+ },
+
+ play: function( action, optionalFadeInDuration ) {
+
+ action.startTime = this.time;
+ this.addAction( action );
+
+ return this;
+
+ },
+
+ fadeOut: function( action, duration ) {
+
+ var keys = [];
+
+ keys.push( { time: this.time, value: 1 } );
+ keys.push( { time: this.time + duration, value: 0 } );
+
+ action.weight = new THREE.NumberKeyframeTrack( "weight", keys );
+
+ return this;
+
+ },
+
+ fadeIn: function( action, duration ) {
+
+ var keys = [];
+
+ keys.push( { time: this.time, value: 0 } );
+ keys.push( { time: this.time + duration, value: 1 } );
+
+ action.weight = new THREE.NumberKeyframeTrack( "weight", keys );
+
+ return this;
+
+ },
+
+ warp: function( action, startTimeScale, endTimeScale, duration ) {
+
+ var keys = [];
+
+ keys.push( { time: this.time, value: startTimeScale } );
+ keys.push( { time: this.time + duration, value: endTimeScale } );
+
+ action.timeScale = new THREE.NumberKeyframeTrack( "timeScale", keys );
+
+ return this;
+
+ },
+
+ crossFade: function( fadeOutAction, fadeInAction, duration, warp ) {
+
+ this.fadeOut( fadeOutAction, duration );
+ this.fadeIn( fadeInAction, duration );
+
+ if ( warp ) {
+
+ var startEndRatio = fadeOutAction.clip.duration / fadeInAction.clip.duration;
+ var endStartRatio = 1.0 / startEndRatio;
+
+ this.warp( fadeOutAction, 1.0, startEndRatio, duration );
+ this.warp( fadeInAction, endStartRatio, 1.0, duration );
+
+ }
+
+ return this;
+
+ },
+
+ update: function( deltaTime ) {
+
+ var mixerDeltaTime = deltaTime * this.timeScale;
+ this.time += mixerDeltaTime;
+
+ for ( var i = 0; i < this.actions.length; i ++ ) {
+
+ var action = this.actions[i];
+
+ var weight = action.getWeightAt( this.time );
+
+ var actionTimeScale = action.getTimeScaleAt( this.time );
+ var actionDeltaTime = mixerDeltaTime * actionTimeScale;
+
+ var actionResults = action.update( actionDeltaTime );
+
+ if ( action.weight <= 0 || ! action.enabled ) continue;
+
+ for ( var j = 0; j < actionResults.length; j ++ ) {
+
+ var name = action.clip.tracks[j].name;
+
+ action.propertyBindings[ j ].accumulate( actionResults[j], weight );
+
+ }
+
+ }
+
+ // apply to nodes
+ for ( var propertyBindingKey in this.propertyBindingMap ) {
+
+ this.propertyBindingMap[ propertyBindingKey ].apply();
+
+ }
+
+ return this;
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.AnimationMixer.prototype );
+
+// File:src/animation/AnimationUtils.js
+
+/**
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.AnimationUtils = {
+
+ getEqualsFunc: function( exemplarValue ) {
+
+ if ( exemplarValue.equals ) {
+ return function equals_object( a, b ) {
+ return a.equals( b );
+ }
+ }
+
+ return function equals_primitive( a, b ) {
+ return ( a === b );
+ };
+
+ },
+
+ clone: function( exemplarValue ) {
+
+ var typeName = typeof exemplarValue;
+ if ( typeName === "object" ) {
+ if ( exemplarValue.clone ) {
+ return exemplarValue.clone();
+ }
+ console.error( "can not figure out how to copy exemplarValue", exemplarValue );
+ }
+
+ return exemplarValue;
+
+ },
+
+ lerp: function( a, b, alpha, interTrack ) {
+
+ var lerpFunc = THREE.AnimationUtils.getLerpFunc( a, interTrack );
+
+ return lerpFunc( a, b, alpha );
+
+ },
+
+ lerp_object: function( a, b, alpha ) {
+ return a.lerp( b, alpha );
+ },
+
+ slerp_object: function( a, b, alpha ) {
+ return a.slerp( b, alpha );
+ },
+
+ lerp_number: function( a, b, alpha ) {
+ return a * ( 1 - alpha ) + b * alpha;
+ },
+
+ lerp_boolean: function( a, b, alpha ) {
+ return ( alpha < 0.5 ) ? a : b;
+ },
+
+ lerp_boolean_immediate: function( a, b, alpha ) {
+ return a;
+ },
+
+ lerp_string: function( a, b, alpha ) {
+ return ( alpha < 0.5 ) ? a : b;
+ },
+
+ lerp_string_immediate: function( a, b, alpha ) {
+ return a;
+ },
+
+ // NOTE: this is an accumulator function that modifies the first argument (e.g. a). This is to minimize memory alocations.
+ getLerpFunc: function( exemplarValue, interTrack ) {
+
+ if ( exemplarValue === undefined || exemplarValue === null ) throw new Error( "examplarValue is null" );
+
+ var typeName = typeof exemplarValue;
+
+ switch( typeName ) {
+
+ case "object":
+ if ( exemplarValue.lerp ) {
+ return THREE.AnimationUtils.lerp_object;
+ }
+
+ if ( exemplarValue.slerp ) {
+ return THREE.AnimationUtils.slerp_object;
+ }
+ break;
+
+ case "number":
+ return THREE.AnimationUtils.lerp_number;
+
+ case "boolean":
+ if ( interTrack ) {
+ return THREE.AnimationUtils.lerp_boolean;
+ } else {
+ return THREE.AnimationUtils.lerp_boolean_immediate;
+ }
+
+ case "string":
+ if ( interTrack ) {
+ return THREE.AnimationUtils.lerp_string;
+ } else {
+ return THREE.AnimationUtils.lerp_string_immediate;
+ }
+
+ }
+
+ }
+
+};
+
+// File:src/animation/KeyframeTrack.js
+
+/**
+ *
+ * A Track that returns a keyframe interpolated value, currently linearly interpolated
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.KeyframeTrack = function ( name, keys ) {
+
+ if ( name === undefined ) throw new Error( "track name is undefined" );
+ if ( keys === undefined || keys.length === 0 ) throw new Error( "no keys in track named " + name );
+
+ this.name = name;
+ this.keys = keys; // time in seconds, value as value
+
+ // the index of the last result, used as a starting point for local search.
+ this.lastIndex = 0;
+
+ this.validate();
+ this.optimize();
+
+};
+
+THREE.KeyframeTrack.prototype = {
+
+ constructor: THREE.KeyframeTrack,
+
+ getAt: function( time ) {
+
+
+ // this can not go higher than this.keys.length.
+ while( ( this.lastIndex < this.keys.length ) && ( time >= this.keys[this.lastIndex].time ) ) {
+ this.lastIndex ++;
+ };
+
+ // this can not go lower than 0.
+ while( ( this.lastIndex > 0 ) && ( time < this.keys[this.lastIndex - 1].time ) ) {
+ this.lastIndex --;
+ }
+
+ if ( this.lastIndex >= this.keys.length ) {
+
+ this.setResult( this.keys[ this.keys.length - 1 ].value );
+
+ return this.result;
+
+ }
+
+ if ( this.lastIndex === 0 ) {
+
+ this.setResult( this.keys[ 0 ].value );
+
+ return this.result;
+
+ }
+
+ var prevKey = this.keys[ this.lastIndex - 1 ];
+ this.setResult( prevKey.value );
+
+ // if true, means that prev/current keys are identical, thus no interpolation required.
+ if ( prevKey.constantToNext ) {
+
+ return this.result;
+
+ }
+
+ // linear interpolation to start with
+ var currentKey = this.keys[ this.lastIndex ];
+ var alpha = ( time - prevKey.time ) / ( currentKey.time - prevKey.time );
+ this.result = this.lerpValues( this.result, currentKey.value, alpha );
+
+ return this.result;
+
+ },
+
+ // move all keyframes either forwards or backwards in time
+ shift: function( timeOffset ) {
+
+ if ( timeOffset !== 0.0 ) {
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+ this.keys[i].time += timeOffset;
+ }
+
+ }
+
+ return this;
+
+ },
+
+ // scale all keyframe times by a factor (useful for frame <-> seconds conversions)
+ scale: function( timeScale ) {
+
+ if ( timeScale !== 1.0 ) {
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+ this.keys[i].time *= timeScale;
+ }
+
+ }
+
+ return this;
+
+ },
+
+ // removes keyframes before and after animation without changing any values within the range [startTime, endTime].
+ // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
+ trim: function( startTime, endTime ) {
+
+ var firstKeysToRemove = 0;
+ for ( var i = 1; i < this.keys.length; i ++ ) {
+ if ( this.keys[i] <= startTime ) {
+ firstKeysToRemove ++;
+ }
+ }
+
+ var lastKeysToRemove = 0;
+ for ( var i = this.keys.length - 2; i > 0; i ++ ) {
+ if ( this.keys[i] >= endTime ) {
+ lastKeysToRemove ++;
+ } else {
+ break;
+ }
+ }
+
+ // remove last keys first because it doesn't affect the position of the first keys (the otherway around doesn't work as easily)
+ if ( ( firstKeysToRemove + lastKeysToRemove ) > 0 ) {
+ this.keys = this.keys.splice( firstKeysToRemove, this.keys.length - lastKeysToRemove - firstKeysToRemove );;
+ }
+
+ return this;
+
+ },
+
+ /* NOTE: This is commented out because we really shouldn't have to handle unsorted key lists
+ Tracks with out of order keys should be considered to be invalid. - bhouston
+ sort: function() {
+
+ this.keys.sort( THREE.KeyframeTrack.keyComparer );
+
+ return this;
+
+ },*/
+
+ // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
+ // One could eventually ensure that all key.values in a track are all of the same type (otherwise interpolation makes no sense.)
+ validate: function() {
+
+ var prevKey = null;
+
+ if ( this.keys.length === 0 ) {
+ console.error( " track is empty, no keys", this );
+ return;
+ }
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+
+ var currKey = this.keys[i];
+
+ if ( ! currKey ) {
+ console.error( " key is null in track", this, i );
+ return;
+ }
+
+ if ( ( typeof currKey.time ) !== 'number' || isNaN( currKey.time ) ) {
+ console.error( " key.time is not a valid number", this, i, currKey );
+ return;
+ }
+
+ if ( currKey.value === undefined || currKey.value === null) {
+ console.error( " key.value is null in track", this, i, currKey );
+ return;
+ }
+
+ if ( prevKey && prevKey.time > currKey.time ) {
+ console.error( " key.time is less than previous key time, out of order keys", this, i, currKey, prevKey );
+ return;
+ }
+
+ prevKey = currKey;
+
+ }
+
+ return this;
+
+ },
+
+ // currently only removes equivalent sequential keys (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0), which are common in morph target animations
+ optimize: function() {
+
+ var newKeys = [];
+ var prevKey = this.keys[0];
+ newKeys.push( prevKey );
+
+ var equalsFunc = THREE.AnimationUtils.getEqualsFunc( prevKey.value );
+
+ for ( var i = 1; i < this.keys.length - 1; i ++ ) {
+ var currKey = this.keys[i];
+ var nextKey = this.keys[i+1];
+
+ // if prevKey & currKey are the same time, remove currKey. If you want immediate adjacent keys, use an epsilon offset
+ // it is not possible to have two keys at the same time as we sort them. The sort is not stable on keys with the same time.
+ if ( ( prevKey.time === currKey.time ) ) {
+
+ continue;
+
+ }
+
+ // remove completely unnecessary keyframes that are the same as their prev and next keys
+ if ( this.compareValues( prevKey.value, currKey.value ) && this.compareValues( currKey.value, nextKey.value ) ) {
+
+ continue;
+
+ }
+
+ // determine if interpolation is required
+ prevKey.constantToNext = this.compareValues( prevKey.value, currKey.value );
+
+ newKeys.push( currKey );
+ prevKey = currKey;
+ }
+ newKeys.push( this.keys[ this.keys.length - 1 ] );
+
+ this.keys = newKeys;
+
+ return this;
+
+ }
+
+};
+
+THREE.KeyframeTrack.keyComparer = function keyComparator(key0, key1) {
+ return key0.time - key1.time;
+};
+
+THREE.KeyframeTrack.parse = function( json ) {
+
+ if ( json.type === undefined ) throw new Error( "track type undefined, can not parse" );
+
+ var trackType = THREE.KeyframeTrack.GetTrackTypeForTypeName( json.type );
+
+ return trackType.parse( json );
+
+};
+
+THREE.KeyframeTrack.GetTrackTypeForTypeName = function( typeName ) {
+ switch( typeName.toLowerCase() ) {
+ case "vector":
+ case "vector2":
+ case "vector3":
+ case "vector4":
+ return THREE.VectorKeyframeTrack;
+
+ case "quaternion":
+ return THREE.QuaternionKeyframeTrack;
+
+ case "integer":
+ case "scalar":
+ case "double":
+ case "float":
+ case "number":
+ return THREE.NumberKeyframeTrack;
+
+ case "bool":
+ case "boolean":
+ return THREE.BooleanKeyframeTrack;
+
+ case "string":
+ return THREE.StringKeyframeTrack;
+ };
+
+ throw new Error( "Unsupported typeName: " + typeName );
+};
+
+// File:src/animation/PropertyBinding.js
+
+/**
+ *
+ * A track bound to a real value in the scene graph.
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.PropertyBinding = function ( rootNode, trackName ) {
+
+ this.rootNode = rootNode;
+ this.trackName = trackName;
+ this.referenceCount = 0;
+ this.originalValue = null; // the value of the property before it was controlled by this binding
+
+ var parseResults = THREE.PropertyBinding.parseTrackName( trackName );
+
+ this.directoryName = parseResults.directoryName;
+ this.nodeName = parseResults.nodeName;
+ this.objectName = parseResults.objectName;
+ this.objectIndex = parseResults.objectIndex;
+ this.propertyName = parseResults.propertyName;
+ this.propertyIndex = parseResults.propertyIndex;
+
+ this.node = THREE.PropertyBinding.findNode( rootNode, this.nodeName ) || rootNode;
+
+ this.cumulativeValue = null;
+ this.cumulativeWeight = 0;
+};
+
+THREE.PropertyBinding.prototype = {
+
+ constructor: THREE.PropertyBinding,
+
+ reset: function() {
+
+ this.cumulativeValue = null;
+ this.cumulativeWeight = 0;
+
+ },
+
+ accumulate: function( value, weight ) {
+
+ if ( ! this.isBound ) this.bind();
+
+ if ( this.cumulativeWeight === 0 ) {
+
+ if ( weight > 0 ) {
+
+ if ( this.cumulativeValue === null ) {
+ this.cumulativeValue = THREE.AnimationUtils.clone( value );
+ }
+ this.cumulativeWeight = weight;
+
+ }
+
+ } else {
+
+ var lerpAlpha = weight / ( this.cumulativeWeight + weight );
+ this.cumulativeValue = this.lerpValue( this.cumulativeValue, value, lerpAlpha );
+ this.cumulativeWeight += weight;
+
+ }
+
+ },
+
+ unbind: function() {
+
+ if ( ! this.isBound ) return;
+
+ this.setValue( this.originalValue );
+
+ this.setValue = null;
+ this.getValue = null;
+ this.lerpValue = null;
+ this.equalsValue = null;
+ this.triggerDirty = null;
+ this.isBound = false;
+
+ },
+
+ // bind to the real property in the scene graph, remember original value, memorize various accessors for speed/inefficiency
+ bind: function() {
+
+ if ( this.isBound ) return;
+
+ var targetObject = this.node;
+
+ // ensure there is a value node
+ if ( ! targetObject ) {
+ console.error( " trying to update node for track: " + this.trackName + " but it wasn't found." );
+ return;
+ }
+
+ if ( this.objectName ) {
+ // special case were we need to reach deeper into the hierarchy to get the face materials....
+ if ( this.objectName === "materials" ) {
+ if ( ! targetObject.material ) {
+ console.error( ' can not bind to material as node does not have a material', this );
+ return;
+ }
+ if ( ! targetObject.material.materials ) {
+ console.error( ' can not bind to material.materials as node.material does not have a materials array', this );
+ return;
+ }
+ targetObject = targetObject.material.materials;
+ } else if ( this.objectName === "bones" ) {
+ if ( ! targetObject.skeleton ) {
+ console.error( ' can not bind to bones as node does not have a skeleton', this );
+ return;
+ }
+ // potential future optimization: skip this if propertyIndex is already an integer, and convert the integer string to a true integer.
+
+ targetObject = targetObject.skeleton.bones;
+
+ // support resolving morphTarget names into indices.
+ for ( var i = 0; i < targetObject.length; i ++ ) {
+ if ( targetObject[i].name === this.objectIndex ) {
+ this.objectIndex = i;
+ break;
+ }
+ }
+ } else {
+
+ if ( targetObject[ this.objectName ] === undefined ) {
+ console.error( ' can not bind to objectName of node, undefined', this );
+ return;
+ }
+ targetObject = targetObject[ this.objectName ];
+ }
+
+ if ( this.objectIndex !== undefined ) {
+ if ( targetObject[ this.objectIndex ] === undefined ) {
+ console.error( " trying to bind to objectIndex of objectName, but is undefined:", this, targetObject );
+ return;
+ }
+
+ targetObject = targetObject[ this.objectIndex ];
+ }
+
+ }
+
+ // special case mappings
+ var nodeProperty = targetObject[ this.propertyName ];
+ if ( ! nodeProperty ) {
+ console.error( " trying to update property for track: " + this.nodeName + '.' + this.propertyName + " but it wasn't found.", targetObject );
+ return;
+ }
+
+ // access a sub element of the property array (only primitives are supported right now)
+ if ( this.propertyIndex !== undefined ) {
+
+ if ( this.propertyName === "morphTargetInfluences" ) {
+ // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer.
+
+ // support resolving morphTarget names into indices.
+ if ( ! targetObject.geometry ) {
+ console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry', this );
+ }
+ if ( ! targetObject.geometry.morphTargets ) {
+ console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this );
+ }
+
+ for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) {
+ if ( targetObject.geometry.morphTargets[i].name === this.propertyIndex ) {
+ this.propertyIndex = i;
+ break;
+ }
+ }
+ }
+
+ this.setValue = function setValue_propertyIndexed( value ) {
+ if ( ! this.equalsValue( nodeProperty[ this.propertyIndex ], value ) ) {
+ nodeProperty[ this.propertyIndex ] = value;
+ return true;
+ }
+ return false;
+ };
+
+ this.getValue = function getValue_propertyIndexed() {
+ return nodeProperty[ this.propertyIndex ];
+ };
+
+ }
+ // must use copy for Object3D.Euler/Quaternion
+ else if ( nodeProperty.copy ) {
+
+ this.setValue = function setValue_propertyObject( value ) {
+ if ( ! this.equalsValue( nodeProperty, value ) ) {
+ nodeProperty.copy( value );
+ return true;
+ }
+ return false;
+ }
+
+ this.getValue = function getValue_propertyObject() {
+ return nodeProperty;
+ };
+
+ }
+ // otherwise just set the property directly on the node (do not use nodeProperty as it may not be a reference object)
+ else {
+
+ this.setValue = function setValue_property( value ) {
+ if ( ! this.equalsValue( targetObject[ this.propertyName ], value ) ) {
+ targetObject[ this.propertyName ] = value;
+ return true;
+ }
+ return false;
+ }
+
+ this.getValue = function getValue_property() {
+ return targetObject[ this.propertyName ];
+ };
+
+ }
+
+ // trigger node dirty
+ if ( targetObject.needsUpdate !== undefined ) { // material
+
+ this.triggerDirty = function triggerDirty_needsUpdate() {
+ this.node.needsUpdate = true;
+ }
+
+ } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform
+
+ this.triggerDirty = function triggerDirty_matrixWorldNeedsUpdate() {
+ targetObject.matrixWorldNeedsUpdate = true;
+ }
+
+ }
+
+ this.originalValue = this.getValue();
+
+ this.equalsValue = THREE.AnimationUtils.getEqualsFunc( this.originalValue );
+ this.lerpValue = THREE.AnimationUtils.getLerpFunc( this.originalValue, true );
+
+ this.isBound = true;
+
+ },
+
+ apply: function() {
+
+ // for speed capture the setter pattern as a closure (sort of a memoization pattern: https://en.wikipedia.org/wiki/Memoization)
+ if ( ! this.isBound ) this.bind();
+
+ // early exit if there is nothing to apply.
+ if ( this.cumulativeWeight > 0 ) {
+
+ // blend with original value
+ if ( this.cumulativeWeight < 1 ) {
+
+ var remainingWeight = 1 - this.cumulativeWeight;
+ var lerpAlpha = remainingWeight / ( this.cumulativeWeight + remainingWeight );
+ this.cumulativeValue = this.lerpValue( this.cumulativeValue, this.originalValue, lerpAlpha );
+
+ }
+
+ var valueChanged = this.setValue( this.cumulativeValue );
+
+ if ( valueChanged && this.triggerDirty ) {
+ this.triggerDirty();
+ }
+
+ // reset accumulator
+ this.cumulativeValue = null;
+ this.cumulativeWeight = 0;
+
+ }
+ }
+
+};
+
+
+THREE.PropertyBinding.parseTrackName = function( trackName ) {
+
+ // matches strings in the form of:
+ // nodeName.property
+ // nodeName.property[accessor]
+ // nodeName.material.property[accessor]
+ // uuid.property[accessor]
+ // uuid.objectName[objectIndex].propertyName[propertyIndex]
+ // parentName/nodeName.property
+ // parentName/parentName/nodeName.property[index]
+ // .bone[Armature.DEF_cog].position
+ // created and tested via https://regex101.com/#javascript
+
+ var re = /^(([\w]+\/)*)([\w-\d]+)?(\.([\w]+)(\[([\w\d\[\]\_. ]+)\])?)?(\.([\w.]+)(\[([\w\d\[\]\_. ]+)\])?)$/;
+ var matches = re.exec(trackName);
+
+ if ( ! matches ) {
+ throw new Error( "cannot parse trackName at all: " + trackName );
+ }
+
+ if (matches.index === re.lastIndex) {
+ re.lastIndex++;
+ }
+
+ var results = {
+ directoryName: matches[1],
+ nodeName: matches[3], // allowed to be null, specified root node.
+ objectName: matches[5],
+ objectIndex: matches[7],
+ propertyName: matches[9],
+ propertyIndex: matches[11] // allowed to be null, specifies that the whole property is set.
+ };
+
+ if ( results.propertyName === null || results.propertyName.length === 0 ) {
+ throw new Error( "can not parse propertyName from trackName: " + trackName );
+ }
+
+ return results;
+
+};
+
+THREE.PropertyBinding.findNode = function( root, nodeName ) {
+
+ function searchSkeleton( skeleton ) {
+
+ for ( var i = 0; i < skeleton.bones.length; i ++ ) {
+
+ var bone = skeleton.bones[i];
+
+ if ( bone.name === nodeName ) {
+
+ return bone;
+
+ }
+ }
+
+ return null;
+
+ }
+
+ function searchNodeSubtree( children ) {
+
+ for ( var i = 0; i < children.length; i ++ ) {
+
+ var childNode = children[i];
+
+ if ( childNode.name === nodeName || childNode.uuid === nodeName ) {
+
+ return childNode;
+
+ }
+
+ var result = searchNodeSubtree( childNode.children );
+
+ if ( result ) return result;
+
+ }
+
+ return null;
+
+ }
+
+ //
+
+ if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) {
+
+ return root;
+
+ }
+
+ // search into skeleton bones.
+ if ( root.skeleton ) {
+
+ var bone = searchSkeleton( root.skeleton );
+
+ if ( bone ) {
+
+ return bone;
+
+ }
+ }
+
+ // search into node subtree.
+ if ( root.children ) {
+
+ var subTreeNode = searchNodeSubtree( root.children );
+
+ if ( subTreeNode ) {
+
+ return subTreeNode;
+
+ }
+
+ }
+
+ return null;
+}
+
+// File:src/animation/tracks/VectorKeyframeTrack.js
+
+/**
+ *
+ * A Track that interpolates Vectors
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.VectorKeyframeTrack = function ( name, keys ) {
+
+ THREE.KeyframeTrack.call( this, name, keys );
+
+ // local cache of value type to avoid allocations during runtime.
+ this.result = this.keys[0].value.clone();
+
+};
+
+THREE.VectorKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );
+
+THREE.VectorKeyframeTrack.prototype.constructor = THREE.VectorKeyframeTrack;
+
+THREE.VectorKeyframeTrack.prototype.setResult = function( value ) {
+
+ this.result.copy( value );
+
+};
+
+// memoization of the lerp function for speed.
+// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
+THREE.VectorKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {
+
+ return value0.lerp( value1, alpha );
+
+};
+
+THREE.VectorKeyframeTrack.prototype.compareValues = function( value0, value1 ) {
+
+ return value0.equals( value1 );
+
+};
+
+THREE.VectorKeyframeTrack.prototype.clone = function() {
+
+ var clonedKeys = [];
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+
+ var key = this.keys[i];
+ clonedKeys.push( {
+ time: key.time,
+ value: key.value.clone()
+ } );
+ }
+
+ return new THREE.VectorKeyframeTrack( this.name, clonedKeys );
+
+};
+
+THREE.VectorKeyframeTrack.parse = function( json ) {
+
+ var elementCount = json.keys[0].value.length;
+ var valueType = THREE[ 'Vector' + elementCount ];
+
+ var keys = [];
+
+ for ( var i = 0; i < json.keys.length; i ++ ) {
+ var jsonKey = json.keys[i];
+ keys.push( {
+ value: new valueType().fromArray( jsonKey.value ),
+ time: jsonKey.time
+ } );
+ }
+
+ return new THREE.VectorKeyframeTrack( json.name, keys );
+
+};
+
+// File:src/animation/tracks/QuaternionKeyframeTrack.js
+
+/**
+ *
+ * A Track that interpolates Quaternion
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.QuaternionKeyframeTrack = function ( name, keys ) {
+
+ THREE.KeyframeTrack.call( this, name, keys );
+
+ // local cache of value type to avoid allocations during runtime.
+ this.result = this.keys[0].value.clone();
+
+};
+
+THREE.QuaternionKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );
+
+THREE.QuaternionKeyframeTrack.prototype.constructor = THREE.QuaternionKeyframeTrack;
+
+THREE.QuaternionKeyframeTrack.prototype.setResult = function( value ) {
+
+ this.result.copy( value );
+
+};
+
+// memoization of the lerp function for speed.
+// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
+THREE.QuaternionKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {
+
+ return value0.slerp( value1, alpha );
+
+};
+
+THREE.QuaternionKeyframeTrack.prototype.compareValues = function( value0, value1 ) {
+
+ return value0.equals( value1 );
+
+};
+
+THREE.QuaternionKeyframeTrack.prototype.multiply = function( quat ) {
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+
+ this.keys[i].value.multiply( quat );
+
+ }
+
+ return this;
+
+};
+
+THREE.QuaternionKeyframeTrack.prototype.clone = function() {
+
+ var clonedKeys = [];
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+
+ var key = this.keys[i];
+ clonedKeys.push( {
+ time: key.time,
+ value: key.value.clone()
+ } );
+ }
+
+ return new THREE.QuaternionKeyframeTrack( this.name, clonedKeys );
+
+};
+
+THREE.QuaternionKeyframeTrack.parse = function( json ) {
+
+ var keys = [];
+
+ for ( var i = 0; i < json.keys.length; i ++ ) {
+ var jsonKey = json.keys[i];
+ keys.push( {
+ value: new THREE.Quaternion().fromArray( jsonKey.value ),
+ time: jsonKey.time
+ } );
+ }
+
+ return new THREE.QuaternionKeyframeTrack( json.name, keys );
+
+};
+
+// File:src/animation/tracks/StringKeyframeTrack.js
+
+/**
+ *
+ * A Track that interpolates Strings
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.StringKeyframeTrack = function ( name, keys ) {
+
+ THREE.KeyframeTrack.call( this, name, keys );
+
+ // local cache of value type to avoid allocations during runtime.
+ this.result = this.keys[0].value;
+
+};
+
+THREE.StringKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );
+
+THREE.StringKeyframeTrack.prototype.constructor = THREE.StringKeyframeTrack;
+
+THREE.StringKeyframeTrack.prototype.setResult = function( value ) {
+
+ this.result = value;
+
+};
+
+// memoization of the lerp function for speed.
+// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
+THREE.StringKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {
+
+ return ( alpha < 1.0 ) ? value0 : value1;
+
+};
+
+THREE.StringKeyframeTrack.prototype.compareValues = function( value0, value1 ) {
+
+ return ( value0 === value1 );
+
+};
+
+THREE.StringKeyframeTrack.prototype.clone = function() {
+
+ var clonedKeys = [];
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+
+ var key = this.keys[i];
+ clonedKeys.push( {
+ time: key.time,
+ value: key.value
+ } );
+ }
+
+ return new THREE.StringKeyframeTrack( this.name, clonedKeys );
+
+};
+
+THREE.StringKeyframeTrack.parse = function( json ) {
+
+ return new THREE.StringKeyframeTrack( json.name, json.keys );
+
+};
+
+// File:src/animation/tracks/BooleanKeyframeTrack.js
+
+/**
+ *
+ * A Track that interpolates Boolean
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.BooleanKeyframeTrack = function ( name, keys ) {
+
+ THREE.KeyframeTrack.call( this, name, keys );
+
+ // local cache of value type to avoid allocations during runtime.
+ this.result = this.keys[0].value;
+
+};
+
+THREE.BooleanKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );
+
+THREE.BooleanKeyframeTrack.prototype.constructor = THREE.BooleanKeyframeTrack;
+
+THREE.BooleanKeyframeTrack.prototype.setResult = function( value ) {
+
+ this.result = value;
+
+};
+
+// memoization of the lerp function for speed.
+// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
+THREE.BooleanKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {
+
+ return ( alpha < 1.0 ) ? value0 : value1;
+
+};
+
+THREE.BooleanKeyframeTrack.prototype.compareValues = function( value0, value1 ) {
+
+ return ( value0 === value1 );
+
+};
+
+THREE.BooleanKeyframeTrack.prototype.clone = function() {
+
+ var clonedKeys = [];
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+
+ var key = this.keys[i];
+ clonedKeys.push( {
+ time: key.time,
+ value: key.value
+ } );
+ }
+
+ return new THREE.BooleanKeyframeTrack( this.name, clonedKeys );
+
+};
+
+THREE.BooleanKeyframeTrack.parse = function( json ) {
+
+ return new THREE.BooleanKeyframeTrack( json.name, json.keys );
+
+};
+
+// File:src/animation/tracks/NumberKeyframeTrack.js
+
+/**
+ *
+ * A Track that interpolates Numbers
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.NumberKeyframeTrack = function ( name, keys ) {
+
+ THREE.KeyframeTrack.call( this, name, keys );
+
+ // local cache of value type to avoid allocations during runtime.
+ this.result = this.keys[0].value;
+
+};
+
+THREE.NumberKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );
+
+THREE.NumberKeyframeTrack.prototype.constructor = THREE.NumberKeyframeTrack;
+
+THREE.NumberKeyframeTrack.prototype.setResult = function( value ) {
+
+ this.result = value;
+
+};
+
+// memoization of the lerp function for speed.
+// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
+THREE.NumberKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {
+
+ return value0 * ( 1 - alpha ) + value1 * alpha;
+
+};
+
+THREE.NumberKeyframeTrack.prototype.compareValues = function( value0, value1 ) {
+
+ return ( value0 === value1 );
+
+};
+
+THREE.NumberKeyframeTrack.prototype.clone = function() {
+
+ var clonedKeys = [];
+
+ for ( var i = 0; i < this.keys.length; i ++ ) {
+
+ var key = this.keys[i];
+ clonedKeys.push( {
+ time: key.time,
+ value: key.value
+ } );
+ }
+
+ return new THREE.NumberKeyframeTrack( this.name, clonedKeys );
+
+};
+
+THREE.NumberKeyframeTrack.parse = function( json ) {
+
+ return new THREE.NumberKeyframeTrack( json.name, json.keys );
+
+};
+
+// File:src/cameras/Camera.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author WestLangley / http://github.com/WestLangley
+*/
+
+THREE.Camera = function () {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Camera';
+
+ this.matrixWorldInverse = new THREE.Matrix4();
+ this.projectionMatrix = new THREE.Matrix4();
+
+};
+
+THREE.Camera.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Camera.prototype.constructor = THREE.Camera;
+
+THREE.Camera.prototype.getWorldDirection = function () {
+
+ var quaternion = new THREE.Quaternion();
+
+ return function ( optionalTarget ) {
+
+ var result = optionalTarget || new THREE.Vector3();
+
+ this.getWorldQuaternion( quaternion );
+
+ return result.set( 0, 0, - 1 ).applyQuaternion( quaternion );
+
+ };
+
+}();
+
+THREE.Camera.prototype.lookAt = function () {
+
+ // This routine does not support cameras with rotated and/or translated parent(s)
+
+ var m1 = new THREE.Matrix4();
+
+ return function ( vector ) {
+
+ m1.lookAt( this.position, vector, this.up );
+
+ this.quaternion.setFromRotationMatrix( m1 );
+
+ };
+
+}();
+
+THREE.Camera.prototype.clone = function () {
+
+ return new this.constructor().copy( this );
+
+};
+
+THREE.Camera.prototype.copy = function ( source ) {
+
+ THREE.Object3D.prototype.copy.call( this, source );
+
+ this.matrixWorldInverse.copy( source.matrixWorldInverse );
+ this.projectionMatrix.copy( source.projectionMatrix );
+
+ return this;
+
+};
+
+// File:src/cameras/CubeCamera.js
+
+/**
+ * Camera for rendering cube maps
+ * - renders scene into axis-aligned cube
+ *
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.CubeCamera = function ( near, far, cubeResolution ) {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'CubeCamera';
+
+ var fov = 90, aspect = 1;
+
+ var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far );
+ cameraPX.up.set( 0, - 1, 0 );
+ cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) );
+ this.add( cameraPX );
+
+ var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far );
+ cameraNX.up.set( 0, - 1, 0 );
+ cameraNX.lookAt( new THREE.Vector3( - 1, 0, 0 ) );
+ this.add( cameraNX );
+
+ var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far );
+ cameraPY.up.set( 0, 0, 1 );
+ cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) );
+ this.add( cameraPY );
+
+ var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far );
+ cameraNY.up.set( 0, 0, - 1 );
+ cameraNY.lookAt( new THREE.Vector3( 0, - 1, 0 ) );
+ this.add( cameraNY );
+
+ var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far );
+ cameraPZ.up.set( 0, - 1, 0 );
+ cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) );
+ this.add( cameraPZ );
+
+ var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far );
+ cameraNZ.up.set( 0, - 1, 0 );
+ cameraNZ.lookAt( new THREE.Vector3( 0, 0, - 1 ) );
+ this.add( cameraNZ );
+
+ this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } );
+
+ this.updateCubeMap = function ( renderer, scene ) {
+
+ if ( this.parent === null ) this.updateMatrixWorld();
+
+ var renderTarget = this.renderTarget;
+ var generateMipmaps = renderTarget.texture.generateMipmaps;
+
+ renderTarget.texture.generateMipmaps = false;
+
+ renderTarget.activeCubeFace = 0;
+ renderer.render( scene, cameraPX, renderTarget );
+
+ renderTarget.activeCubeFace = 1;
+ renderer.render( scene, cameraNX, renderTarget );
+
+ renderTarget.activeCubeFace = 2;
+ renderer.render( scene, cameraPY, renderTarget );
+
+ renderTarget.activeCubeFace = 3;
+ renderer.render( scene, cameraNY, renderTarget );
+
+ renderTarget.activeCubeFace = 4;
+ renderer.render( scene, cameraPZ, renderTarget );
+
+ renderTarget.texture.generateMipmaps = generateMipmaps;
+
+ renderTarget.activeCubeFace = 5;
+ renderer.render( scene, cameraNZ, renderTarget );
+
+ renderer.setRenderTarget( null );
+
+ };
+
+};
+
+THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype );
+THREE.CubeCamera.prototype.constructor = THREE.CubeCamera;
+
+// File:src/cameras/OrthographicCamera.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) {
+
+ THREE.Camera.call( this );
+
+ this.type = 'OrthographicCamera';
+
+ this.zoom = 1;
+
+ this.left = left;
+ this.right = right;
+ this.top = top;
+ this.bottom = bottom;
+
+ this.near = ( near !== undefined ) ? near : 0.1;
+ this.far = ( far !== undefined ) ? far : 2000;
+
+ this.updateProjectionMatrix();
+
+};
+
+THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype );
+THREE.OrthographicCamera.prototype.constructor = THREE.OrthographicCamera;
+
+THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () {
+
+ var dx = ( this.right - this.left ) / ( 2 * this.zoom );
+ var dy = ( this.top - this.bottom ) / ( 2 * this.zoom );
+ var cx = ( this.right + this.left ) / 2;
+ var cy = ( this.top + this.bottom ) / 2;
+
+ this.projectionMatrix.makeOrthographic( cx - dx, cx + dx, cy + dy, cy - dy, this.near, this.far );
+
+};
+
+THREE.OrthographicCamera.prototype.copy = function ( source ) {
+
+ THREE.Camera.prototype.copy.call( this, source );
+
+ this.left = source.left;
+ this.right = source.right;
+ this.top = source.top;
+ this.bottom = source.bottom;
+ this.near = source.near;
+ this.far = source.far;
+
+ this.zoom = source.zoom;
+
+ return this;
+
+};
+
+THREE.OrthographicCamera.prototype.toJSON = function ( meta ) {
+
+ var data = THREE.Object3D.prototype.toJSON.call( this, meta );
+
+ data.object.zoom = this.zoom;
+ data.object.left = this.left;
+ data.object.right = this.right;
+ data.object.top = this.top;
+ data.object.bottom = this.bottom;
+ data.object.near = this.near;
+ data.object.far = this.far;
+
+ return data;
+
+};
+
+// File:src/cameras/PerspectiveCamera.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author greggman / http://games.greggman.com/
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ */
+
+THREE.PerspectiveCamera = function ( fov, aspect, near, far ) {
+
+ THREE.Camera.call( this );
+
+ this.type = 'PerspectiveCamera';
+
+ this.zoom = 1;
+
+ this.fov = fov !== undefined ? fov : 50;
+ this.aspect = aspect !== undefined ? aspect : 1;
+ this.near = near !== undefined ? near : 0.1;
+ this.far = far !== undefined ? far : 2000;
+
+ this.updateProjectionMatrix();
+
+};
+
+THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype );
+THREE.PerspectiveCamera.prototype.constructor = THREE.PerspectiveCamera;
+
+
+/**
+ * Uses Focal Length (in mm) to estimate and set FOV
+ * 35mm (full-frame) camera is used if frame size is not specified;
+ * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html
+ */
+
+THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) {
+
+ if ( frameHeight === undefined ) frameHeight = 24;
+
+ this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) );
+ this.updateProjectionMatrix();
+
+};
+
+
+/**
+ * Sets an offset in a larger frustum. This is useful for multi-window or
+ * multi-monitor/multi-machine setups.
+ *
+ * For example, if you have 3x2 monitors and each monitor is 1920x1080 and
+ * the monitors are in grid like this
+ *
+ * +---+---+---+
+ * | A | B | C |
+ * +---+---+---+
+ * | D | E | F |
+ * +---+---+---+
+ *
+ * then for each monitor you would call it like this
+ *
+ * var w = 1920;
+ * var h = 1080;
+ * var fullWidth = w * 3;
+ * var fullHeight = h * 2;
+ *
+ * --A--
+ * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );
+ * --B--
+ * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );
+ * --C--
+ * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );
+ * --D--
+ * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );
+ * --E--
+ * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );
+ * --F--
+ * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );
+ *
+ * Note there is no reason monitors have to be the same size or in a grid.
+ */
+
+THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) {
+
+ this.fullWidth = fullWidth;
+ this.fullHeight = fullHeight;
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+
+ this.updateProjectionMatrix();
+
+};
+
+
+THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () {
+
+ var fov = THREE.Math.radToDeg( 2 * Math.atan( Math.tan( THREE.Math.degToRad( this.fov ) * 0.5 ) / this.zoom ) );
+
+ if ( this.fullWidth ) {
+
+ var aspect = this.fullWidth / this.fullHeight;
+ var top = Math.tan( THREE.Math.degToRad( fov * 0.5 ) ) * this.near;
+ var bottom = - top;
+ var left = aspect * bottom;
+ var right = aspect * top;
+ var width = Math.abs( right - left );
+ var height = Math.abs( top - bottom );
+
+ this.projectionMatrix.makeFrustum(
+ left + this.x * width / this.fullWidth,
+ left + ( this.x + this.width ) * width / this.fullWidth,
+ top - ( this.y + this.height ) * height / this.fullHeight,
+ top - this.y * height / this.fullHeight,
+ this.near,
+ this.far
+ );
+
+ } else {
+
+ this.projectionMatrix.makePerspective( fov, this.aspect, this.near, this.far );
+
+ }
+
+};
+
+THREE.PerspectiveCamera.prototype.copy = function ( source ) {
+
+ THREE.Camera.prototype.copy.call( this, source );
+
+ this.fov = source.fov;
+ this.aspect = source.aspect;
+ this.near = source.near;
+ this.far = source.far;
+
+ this.zoom = source.zoom;
+
+ return this;
+
+};
+
+THREE.PerspectiveCamera.prototype.toJSON = function ( meta ) {
+
+ var data = THREE.Object3D.prototype.toJSON.call( this, meta );
+
+ data.object.zoom = this.zoom;
+ data.object.fov = this.fov;
+ data.object.aspect = this.aspect;
+ data.object.near = this.near;
+ data.object.far = this.far;
+
+ return data;
+
+};
+
+// File:src/lights/Light.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Light = function ( color ) {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Light';
+
+ this.color = new THREE.Color( color );
+
+ this.receiveShadow = undefined;
+
+};
+
+THREE.Light.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Light.prototype.constructor = THREE.Light;
+
+Object.defineProperties( THREE.Light.prototype, {
+ onlyShadow: {
+ set: function ( value ) {
+ console.warn( 'THREE.Light: .onlyShadow has been removed.' );
+ }
+ },
+ shadowCameraFov: {
+ set: function ( value ) {
+ this.shadow.camera.fov = value;
+ }
+ },
+ shadowCameraLeft: {
+ set: function ( value ) {
+ this.shadow.camera.left = value;
+ }
+ },
+ shadowCameraRight: {
+ set: function ( value ) {
+ this.shadow.camera.right = value;
+ }
+ },
+ shadowCameraTop: {
+ set: function ( value ) {
+ this.shadow.camera.top = value;
+ }
+ },
+ shadowCameraBottom: {
+ set: function ( value ) {
+ this.shadow.camera.bottom = value;
+ }
+ },
+ shadowCameraNear: {
+ set: function ( value ) {
+ this.shadow.camera.near = value;
+ }
+ },
+ shadowCameraFar: {
+ set: function ( value ) {
+ this.shadow.camera.far = value;
+ }
+ },
+ shadowCameraVisible: {
+ set: function ( value ) {
+ console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow ) instead.' );
+ }
+ },
+ shadowBias: {
+ set: function ( value ) {
+ this.shadow.bias = value;
+ }
+ },
+ shadowDarkness: {
+ set: function ( value ) {
+ this.shadow.darkness = value;
+ }
+ },
+ shadowMapWidth: {
+ set: function ( value ) {
+ this.shadow.mapSize.width = value;
+ }
+ },
+ shadowMapHeight: {
+ set: function ( value ) {
+ this.shadow.mapSize.height = value;
+ }
+ }
+} );
+
+THREE.Light.prototype.copy = function ( source ) {
+
+ THREE.Object3D.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+
+ return this;
+
+};
+
+THREE.Light.prototype.toJSON = function ( meta ) {
+
+ var data = THREE.Object3D.prototype.toJSON.call( this, meta );
+
+ data.object.color = this.color.getHex();
+ if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex();
+
+ if ( this.intensity !== undefined ) data.object.intensity = this.intensity;
+ if ( this.distance !== undefined ) data.object.distance = this.distance;
+ if ( this.angle !== undefined ) data.object.angle = this.angle;
+ if ( this.decay !== undefined ) data.object.decay = this.decay;
+ if ( this.exponent !== undefined ) data.object.exponent = this.exponent;
+
+ return data;
+
+};
+
+// File:src/lights/LightShadow.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.LightShadow = function ( camera ) {
+
+ this.camera = camera;
+
+ this.bias = 0;
+ this.darkness = 1;
+
+ this.mapSize = new THREE.Vector2( 512, 512 );
+
+ this.map = null;
+ this.matrix = null;
+
+};
+
+THREE.LightShadow.prototype = {
+
+ constructor: THREE.LightShadow,
+
+ copy: function ( source ) {
+
+ this.camera = source.camera.clone();
+
+ this.bias = source.bias;
+ this.darkness = source.darkness;
+
+ this.mapSize.copy( source.mapSize );
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ }
+
+};
+
+// File:src/lights/AmbientLight.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.AmbientLight = function ( color ) {
+
+ THREE.Light.call( this, color );
+
+ this.type = 'AmbientLight';
+
+ this.castShadow = undefined;
+
+};
+
+THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype );
+THREE.AmbientLight.prototype.constructor = THREE.AmbientLight;
+
+// File:src/lights/DirectionalLight.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.DirectionalLight = function ( color, intensity ) {
+
+ THREE.Light.call( this, color );
+
+ this.type = 'DirectionalLight';
+
+ this.position.set( 0, 1, 0 );
+ this.updateMatrix();
+
+ this.target = new THREE.Object3D();
+
+ this.intensity = ( intensity !== undefined ) ? intensity : 1;
+
+ this.shadow = new THREE.LightShadow( new THREE.OrthographicCamera( - 500, 500, 500, - 500, 50, 5000 ) );
+
+};
+
+THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype );
+THREE.DirectionalLight.prototype.constructor = THREE.DirectionalLight;
+
+THREE.DirectionalLight.prototype.copy = function ( source ) {
+
+ THREE.Light.prototype.copy.call( this, source );
+
+ this.intensity = source.intensity;
+ this.target = source.target.clone();
+
+ this.shadow = source.shadow.clone();
+
+ return this;
+
+};
+
+// File:src/lights/HemisphereLight.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) {
+
+ THREE.Light.call( this, skyColor );
+
+ this.type = 'HemisphereLight';
+
+ this.castShadow = undefined;
+
+ this.position.set( 0, 1, 0 );
+ this.updateMatrix();
+
+ this.groundColor = new THREE.Color( groundColor );
+ this.intensity = ( intensity !== undefined ) ? intensity : 1;
+
+};
+
+THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype );
+THREE.HemisphereLight.prototype.constructor = THREE.HemisphereLight;
+
+THREE.HemisphereLight.prototype.copy = function ( source ) {
+
+ THREE.Light.prototype.copy.call( this, source );
+
+ this.groundColor.copy( source.groundColor );
+ this.intensity = source.intensity;
+
+ return this;
+
+};
+
+// File:src/lights/PointLight.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+
+THREE.PointLight = function ( color, intensity, distance, decay ) {
+
+ THREE.Light.call( this, color );
+
+ this.type = 'PointLight';
+
+ this.intensity = ( intensity !== undefined ) ? intensity : 1;
+ this.distance = ( distance !== undefined ) ? distance : 0;
+ this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2.
+
+ this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 90, 1, 1, 500 ) );
+
+};
+
+THREE.PointLight.prototype = Object.create( THREE.Light.prototype );
+THREE.PointLight.prototype.constructor = THREE.PointLight;
+
+THREE.PointLight.prototype.copy = function ( source ) {
+
+ THREE.Light.prototype.copy.call( this, source );
+
+ this.intensity = source.intensity;
+ this.distance = source.distance;
+ this.decay = source.decay;
+
+ this.shadow = source.shadow.clone();
+
+ return this;
+
+};
+
+// File:src/lights/SpotLight.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SpotLight = function ( color, intensity, distance, angle, exponent, decay ) {
+
+ THREE.Light.call( this, color );
+
+ this.type = 'SpotLight';
+
+ this.position.set( 0, 1, 0 );
+ this.updateMatrix();
+
+ this.target = new THREE.Object3D();
+
+ this.intensity = ( intensity !== undefined ) ? intensity : 1;
+ this.distance = ( distance !== undefined ) ? distance : 0;
+ this.angle = ( angle !== undefined ) ? angle : Math.PI / 3;
+ this.exponent = ( exponent !== undefined ) ? exponent : 10;
+ this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2.
+
+ this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 50, 1, 50, 5000 ) );
+
+};
+
+THREE.SpotLight.prototype = Object.create( THREE.Light.prototype );
+THREE.SpotLight.prototype.constructor = THREE.SpotLight;
+
+THREE.SpotLight.prototype.copy = function ( source ) {
+
+ THREE.Light.prototype.copy.call( this, source );
+
+ this.intensity = source.intensity;
+ this.distance = source.distance;
+ this.angle = source.angle;
+ this.exponent = source.exponent;
+ this.decay = source.decay;
+
+ this.target = source.target.clone();
+
+ this.shadow = source.shadow.clone();
+
+ return this;
+
+};
+
+// File:src/loaders/Cache.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Cache = {
+
+ enabled: false,
+
+ files: {},
+
+ add: function ( key, file ) {
+
+ if ( this.enabled === false ) return;
+
+ // console.log( 'THREE.Cache', 'Adding key:', key );
+
+ this.files[ key ] = file;
+
+ },
+
+ get: function ( key ) {
+
+ if ( this.enabled === false ) return;
+
+ // console.log( 'THREE.Cache', 'Checking key:', key );
+
+ return this.files[ key ];
+
+ },
+
+ remove: function ( key ) {
+
+ delete this.files[ key ];
+
+ },
+
+ clear: function () {
+
+ this.files = {};
+
+ }
+
+};
+
+// File:src/loaders/Loader.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Loader = function () {
+
+ this.onLoadStart = function () {};
+ this.onLoadProgress = function () {};
+ this.onLoadComplete = function () {};
+
+};
+
+THREE.Loader.prototype = {
+
+ constructor: THREE.Loader,
+
+ crossOrigin: undefined,
+
+ extractUrlBase: function ( url ) {
+
+ var parts = url.split( '/' );
+
+ if ( parts.length === 1 ) return './';
+
+ parts.pop();
+
+ return parts.join( '/' ) + '/';
+
+ },
+
+ initMaterials: function ( materials, texturePath, crossOrigin ) {
+
+ var array = [];
+
+ for ( var i = 0; i < materials.length; ++ i ) {
+
+ array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin );
+
+ }
+
+ return array;
+
+ },
+
+ createMaterial: ( function () {
+
+ var color, textureLoader, materialLoader;
+
+ return function ( m, texturePath, crossOrigin ) {
+
+ if ( color === undefined ) color = new THREE.Color();
+ if ( textureLoader === undefined ) textureLoader = new THREE.TextureLoader();
+ if ( materialLoader === undefined ) materialLoader = new THREE.MaterialLoader();
+
+ // convert from old material format
+
+ var textures = {};
+
+ function loadTexture( path, repeat, offset, wrap, anisotropy ) {
+
+ var fullPath = texturePath + path;
+ var loader = THREE.Loader.Handlers.get( fullPath );
+
+ var texture;
+
+ if ( loader !== null ) {
+
+ texture = loader.load( fullPath );
+
+ } else {
+
+ textureLoader.setCrossOrigin( crossOrigin );
+ texture = textureLoader.load( fullPath );
+
+ }
+
+ if ( repeat !== undefined ) {
+
+ texture.repeat.fromArray( repeat );
+
+ if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping;
+ if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping;
+
+ }
+
+ if ( offset !== undefined ) {
+
+ texture.offset.fromArray( offset );
+
+ }
+
+ if ( wrap !== undefined ) {
+
+ if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = THREE.RepeatWrapping;
+ if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = THREE.MirroredRepeatWrapping;
+
+ if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = THREE.RepeatWrapping;
+ if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = THREE.MirroredRepeatWrapping;
+
+ }
+
+ if ( anisotropy !== undefined ) {
+
+ texture.anisotropy = anisotropy;
+
+ }
+
+ var uuid = THREE.Math.generateUUID();
+
+ textures[ uuid ] = texture;
+
+ return uuid;
+
+ }
+
+ //
+
+ var json = {
+ uuid: THREE.Math.generateUUID(),
+ type: 'MeshLambertMaterial'
+ };
+
+ for ( var name in m ) {
+
+ var value = m[ name ];
+
+ switch ( name ) {
+ case 'DbgColor':
+ json.color = value;
+ break;
+ case 'DbgIndex':
+ case 'opticalDensity':
+ case 'illumination':
+ // These were never supported
+ break;
+ case 'DbgName':
+ json.name = value;
+ break;
+ case 'blending':
+ json.blending = THREE[ value ];
+ break;
+ case 'colorDiffuse':
+ json.color = color.fromArray( value ).getHex();
+ break;
+ case 'colorSpecular':
+ json.specular = color.fromArray( value ).getHex();
+ break;
+ case 'colorEmissive':
+ json.emissive = color.fromArray( value ).getHex();
+ break;
+ case 'specularCoef':
+ json.shininess = value;
+ break;
+ case 'shading':
+ if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial';
+ if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial';
+ break;
+ case 'mapDiffuse':
+ json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy );
+ break;
+ case 'mapDiffuseRepeat':
+ case 'mapDiffuseOffset':
+ case 'mapDiffuseWrap':
+ case 'mapDiffuseAnisotropy':
+ break;
+ case 'mapLight':
+ json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy );
+ break;
+ case 'mapLightRepeat':
+ case 'mapLightOffset':
+ case 'mapLightWrap':
+ case 'mapLightAnisotropy':
+ break;
+ case 'mapAO':
+ json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy );
+ break;
+ case 'mapAORepeat':
+ case 'mapAOOffset':
+ case 'mapAOWrap':
+ case 'mapAOAnisotropy':
+ break;
+ case 'mapBump':
+ json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy );
+ break;
+ case 'mapBumpScale':
+ json.bumpScale = value;
+ break;
+ case 'mapBumpRepeat':
+ case 'mapBumpOffset':
+ case 'mapBumpWrap':
+ case 'mapBumpAnisotropy':
+ break;
+ case 'mapNormal':
+ json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy );
+ break;
+ case 'mapNormalFactor':
+ json.normalScale = [ value, value ];
+ break;
+ case 'mapNormalRepeat':
+ case 'mapNormalOffset':
+ case 'mapNormalWrap':
+ case 'mapNormalAnisotropy':
+ break;
+ case 'mapSpecular':
+ json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy );
+ break;
+ case 'mapSpecularRepeat':
+ case 'mapSpecularOffset':
+ case 'mapSpecularWrap':
+ case 'mapSpecularAnisotropy':
+ break;
+ case 'mapAlpha':
+ json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy );
+ break;
+ case 'mapAlphaRepeat':
+ case 'mapAlphaOffset':
+ case 'mapAlphaWrap':
+ case 'mapAlphaAnisotropy':
+ break;
+ case 'flipSided':
+ json.side = THREE.BackSide;
+ break;
+ case 'doubleSided':
+ json.side = THREE.DoubleSide;
+ break;
+ case 'transparency':
+ console.warn( 'THREE.Loader: transparency has been renamed to opacity' );
+ json.opacity = value;
+ break;
+ case 'opacity':
+ case 'transparent':
+ case 'depthTest':
+ case 'depthWrite':
+ case 'transparent':
+ case 'visible':
+ case 'wireframe':
+ json[ name ] = value;
+ break;
+ case 'vertexColors':
+ if ( value === true ) json.vertexColors = THREE.VertexColors;
+ if ( value === 'face' ) json.vertexColors = THREE.FaceColors;
+ break;
+ default:
+ console.error( 'Loader.createMaterial: Unsupported', name, value );
+ break;
+ }
+
+ }
+
+ if ( json.type !== 'MeshPhongMaterial' ) delete json.specular;
+ if ( json.opacity < 1 ) json.transparent = true;
+
+ materialLoader.setTextures( textures );
+
+ return materialLoader.parse( json );
+
+ };
+
+ } )()
+
+};
+
+THREE.Loader.Handlers = {
+
+ handlers: [],
+
+ add: function ( regex, loader ) {
+
+ this.handlers.push( regex, loader );
+
+ },
+
+ get: function ( file ) {
+
+ var handlers = this.handlers;
+
+ for ( var i = 0, l = handlers.length; i < l; i += 2 ) {
+
+ var regex = handlers[ i ];
+ var loader = handlers[ i + 1 ];
+
+ if ( regex.test( file ) ) {
+
+ return loader;
+
+ }
+
+ }
+
+ return null;
+
+ }
+
+};
+
+// File:src/loaders/XHRLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.XHRLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.XHRLoader.prototype = {
+
+ constructor: THREE.XHRLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var cached = THREE.Cache.get( url );
+
+ if ( cached !== undefined ) {
+
+ if ( onLoad ) {
+
+ setTimeout( function () {
+
+ onLoad( cached );
+
+ }, 0 );
+
+ }
+
+ return cached;
+
+ }
+
+ var request = new XMLHttpRequest();
+ request.open( 'GET', url, true );
+
+ request.addEventListener( 'load', function ( event ) {
+
+ var response = event.target.response;
+
+ THREE.Cache.add( url, response );
+
+ if ( onLoad ) onLoad( response );
+
+ scope.manager.itemEnd( url );
+
+ }, false );
+
+ if ( onProgress !== undefined ) {
+
+ request.addEventListener( 'progress', function ( event ) {
+
+ onProgress( event );
+
+ }, false );
+
+ }
+
+ request.addEventListener( 'error', function ( event ) {
+
+ if ( onError ) onError( event );
+
+ scope.manager.itemError( url );
+
+ }, false );
+
+ if ( this.crossOrigin !== undefined ) request.crossOrigin = this.crossOrigin;
+ if ( this.responseType !== undefined ) request.responseType = this.responseType;
+ if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials;
+
+ request.send( null );
+
+ scope.manager.itemStart( url );
+
+ return request;
+
+ },
+
+ setResponseType: function ( value ) {
+
+ this.responseType = value;
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ },
+
+ setWithCredentials: function ( value ) {
+
+ this.withCredentials = value;
+
+ }
+
+};
+
+// File:src/loaders/ImageLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.ImageLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.ImageLoader.prototype = {
+
+ constructor: THREE.ImageLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var cached = THREE.Cache.get( url );
+
+ if ( cached !== undefined ) {
+
+ scope.manager.itemStart( url );
+
+ if ( onLoad ) {
+
+ setTimeout( function () {
+
+ onLoad( cached );
+
+ scope.manager.itemEnd( url );
+
+ }, 0 );
+
+ } else {
+
+ scope.manager.itemEnd( url );
+
+ }
+
+ return cached;
+
+ }
+
+ var image = document.createElement( 'img' );
+
+ image.addEventListener( 'load', function ( event ) {
+
+ THREE.Cache.add( url, this );
+
+ if ( onLoad ) onLoad( this );
+
+ scope.manager.itemEnd( url );
+
+ }, false );
+
+ if ( onProgress !== undefined ) {
+
+ image.addEventListener( 'progress', function ( event ) {
+
+ onProgress( event );
+
+ }, false );
+
+ }
+
+ image.addEventListener( 'error', function ( event ) {
+
+ if ( onError ) onError( event );
+
+ scope.manager.itemError( url );
+
+ }, false );
+
+ if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin;
+
+ scope.manager.itemStart( url );
+
+ image.src = url;
+
+ return image;
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ }
+
+};
+
+// File:src/loaders/JSONLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.JSONLoader = function ( manager ) {
+
+ if ( typeof manager === 'boolean' ) {
+
+ console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' );
+ manager = undefined;
+
+ }
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+ this.withCredentials = false;
+
+};
+
+THREE.JSONLoader.prototype = {
+
+ constructor: THREE.JSONLoader,
+
+ // Deprecated
+
+ get statusDomElement () {
+
+ if ( this._statusDomElement === undefined ) {
+
+ this._statusDomElement = document.createElement( 'div' );
+
+ }
+
+ console.warn( 'THREE.JSONLoader: .statusDomElement has been removed.' );
+ return this._statusDomElement;
+
+ },
+
+ load: function( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : THREE.Loader.prototype.extractUrlBase( url );
+
+ var loader = new THREE.XHRLoader( this.manager );
+ loader.setCrossOrigin( this.crossOrigin );
+ loader.setWithCredentials( this.withCredentials );
+ loader.load( url, function ( text ) {
+
+ var json = JSON.parse( text );
+ var metadata = json.metadata;
+
+ if ( metadata !== undefined ) {
+
+ if ( metadata.type === 'object' ) {
+
+ console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' );
+ return;
+
+ }
+
+ if ( metadata.type === 'scene' ) {
+
+ console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' );
+ return;
+
+ }
+
+ }
+
+ var object = scope.parse( json, texturePath );
+ onLoad( object.geometry, object.materials );
+
+ } );
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ },
+
+ setTexturePath: function ( value ) {
+
+ this.texturePath = value;
+
+ },
+
+ parse: function ( json, texturePath ) {
+
+ var geometry = new THREE.Geometry(),
+ scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0;
+
+ parseModel( scale );
+
+ parseSkin();
+ parseMorphing( scale );
+ parseAnimations();
+
+ geometry.computeFaceNormals();
+ geometry.computeBoundingSphere();
+
+ function parseModel( scale ) {
+
+ function isBitSet( value, position ) {
+
+ return value & ( 1 << position );
+
+ }
+
+ var i, j, fi,
+
+ offset, zLength,
+
+ colorIndex, normalIndex, uvIndex, materialIndex,
+
+ type,
+ isQuad,
+ hasMaterial,
+ hasFaceVertexUv,
+ hasFaceNormal, hasFaceVertexNormal,
+ hasFaceColor, hasFaceVertexColor,
+
+ vertex, face, faceA, faceB, hex, normal,
+
+ uvLayer, uv, u, v,
+
+ faces = json.faces,
+ vertices = json.vertices,
+ normals = json.normals,
+ colors = json.colors,
+
+ nUvLayers = 0;
+
+ if ( json.uvs !== undefined ) {
+
+ // disregard empty arrays
+
+ for ( i = 0; i < json.uvs.length; i ++ ) {
+
+ if ( json.uvs[ i ].length ) nUvLayers ++;
+
+ }
+
+ for ( i = 0; i < nUvLayers; i ++ ) {
+
+ geometry.faceVertexUvs[ i ] = [];
+
+ }
+
+ }
+
+ offset = 0;
+ zLength = vertices.length;
+
+ while ( offset < zLength ) {
+
+ vertex = new THREE.Vector3();
+
+ vertex.x = vertices[ offset ++ ] * scale;
+ vertex.y = vertices[ offset ++ ] * scale;
+ vertex.z = vertices[ offset ++ ] * scale;
+
+ geometry.vertices.push( vertex );
+
+ }
+
+ offset = 0;
+ zLength = faces.length;
+
+ while ( offset < zLength ) {
+
+ type = faces[ offset ++ ];
+
+
+ isQuad = isBitSet( type, 0 );
+ hasMaterial = isBitSet( type, 1 );
+ hasFaceVertexUv = isBitSet( type, 3 );
+ hasFaceNormal = isBitSet( type, 4 );
+ hasFaceVertexNormal = isBitSet( type, 5 );
+ hasFaceColor = isBitSet( type, 6 );
+ hasFaceVertexColor = isBitSet( type, 7 );
+
+ // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);
+
+ if ( isQuad ) {
+
+ faceA = new THREE.Face3();
+ faceA.a = faces[ offset ];
+ faceA.b = faces[ offset + 1 ];
+ faceA.c = faces[ offset + 3 ];
+
+ faceB = new THREE.Face3();
+ faceB.a = faces[ offset + 1 ];
+ faceB.b = faces[ offset + 2 ];
+ faceB.c = faces[ offset + 3 ];
+
+ offset += 4;
+
+ if ( hasMaterial ) {
+
+ materialIndex = faces[ offset ++ ];
+ faceA.materialIndex = materialIndex;
+ faceB.materialIndex = materialIndex;
+
+ }
+
+ // to get face <=> uv index correspondence
+
+ fi = geometry.faces.length;
+
+ if ( hasFaceVertexUv ) {
+
+ for ( i = 0; i < nUvLayers; i ++ ) {
+
+ uvLayer = json.uvs[ i ];
+
+ geometry.faceVertexUvs[ i ][ fi ] = [];
+ geometry.faceVertexUvs[ i ][ fi + 1 ] = [];
+
+ for ( j = 0; j < 4; j ++ ) {
+
+ uvIndex = faces[ offset ++ ];
+
+ u = uvLayer[ uvIndex * 2 ];
+ v = uvLayer[ uvIndex * 2 + 1 ];
+
+ uv = new THREE.Vector2( u, v );
+
+ if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv );
+ if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv );
+
+ }
+
+ }
+
+ }
+
+ if ( hasFaceNormal ) {
+
+ normalIndex = faces[ offset ++ ] * 3;
+
+ faceA.normal.set(
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ]
+ );
+
+ faceB.normal.copy( faceA.normal );
+
+ }
+
+ if ( hasFaceVertexNormal ) {
+
+ for ( i = 0; i < 4; i ++ ) {
+
+ normalIndex = faces[ offset ++ ] * 3;
+
+ normal = new THREE.Vector3(
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ]
+ );
+
+
+ if ( i !== 2 ) faceA.vertexNormals.push( normal );
+ if ( i !== 0 ) faceB.vertexNormals.push( normal );
+
+ }
+
+ }
+
+
+ if ( hasFaceColor ) {
+
+ colorIndex = faces[ offset ++ ];
+ hex = colors[ colorIndex ];
+
+ faceA.color.setHex( hex );
+ faceB.color.setHex( hex );
+
+ }
+
+
+ if ( hasFaceVertexColor ) {
+
+ for ( i = 0; i < 4; i ++ ) {
+
+ colorIndex = faces[ offset ++ ];
+ hex = colors[ colorIndex ];
+
+ if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) );
+ if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) );
+
+ }
+
+ }
+
+ geometry.faces.push( faceA );
+ geometry.faces.push( faceB );
+
+ } else {
+
+ face = new THREE.Face3();
+ face.a = faces[ offset ++ ];
+ face.b = faces[ offset ++ ];
+ face.c = faces[ offset ++ ];
+
+ if ( hasMaterial ) {
+
+ materialIndex = faces[ offset ++ ];
+ face.materialIndex = materialIndex;
+
+ }
+
+ // to get face <=> uv index correspondence
+
+ fi = geometry.faces.length;
+
+ if ( hasFaceVertexUv ) {
+
+ for ( i = 0; i < nUvLayers; i ++ ) {
+
+ uvLayer = json.uvs[ i ];
+
+ geometry.faceVertexUvs[ i ][ fi ] = [];
+
+ for ( j = 0; j < 3; j ++ ) {
+
+ uvIndex = faces[ offset ++ ];
+
+ u = uvLayer[ uvIndex * 2 ];
+ v = uvLayer[ uvIndex * 2 + 1 ];
+
+ uv = new THREE.Vector2( u, v );
+
+ geometry.faceVertexUvs[ i ][ fi ].push( uv );
+
+ }
+
+ }
+
+ }
+
+ if ( hasFaceNormal ) {
+
+ normalIndex = faces[ offset ++ ] * 3;
+
+ face.normal.set(
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ]
+ );
+
+ }
+
+ if ( hasFaceVertexNormal ) {
+
+ for ( i = 0; i < 3; i ++ ) {
+
+ normalIndex = faces[ offset ++ ] * 3;
+
+ normal = new THREE.Vector3(
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ++ ],
+ normals[ normalIndex ]
+ );
+
+ face.vertexNormals.push( normal );
+
+ }
+
+ }
+
+
+ if ( hasFaceColor ) {
+
+ colorIndex = faces[ offset ++ ];
+ face.color.setHex( colors[ colorIndex ] );
+
+ }
+
+
+ if ( hasFaceVertexColor ) {
+
+ for ( i = 0; i < 3; i ++ ) {
+
+ colorIndex = faces[ offset ++ ];
+ face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) );
+
+ }
+
+ }
+
+ geometry.faces.push( face );
+
+ }
+
+ }
+
+ };
+
+ function parseSkin() {
+
+ var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2;
+
+ if ( json.skinWeights ) {
+
+ for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) {
+
+ var x = json.skinWeights[ i ];
+ var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0;
+ var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0;
+ var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0;
+
+ geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) );
+
+ }
+
+ }
+
+ if ( json.skinIndices ) {
+
+ for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) {
+
+ var a = json.skinIndices[ i ];
+ var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0;
+ var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0;
+ var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0;
+
+ geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) );
+
+ }
+
+ }
+
+ geometry.bones = json.bones;
+
+ if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) {
+
+ console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' +
+ geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' );
+
+ }
+
+ };
+
+ function parseMorphing( scale ) {
+
+ if ( json.morphTargets !== undefined ) {
+
+ for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) {
+
+ geometry.morphTargets[ i ] = {};
+ geometry.morphTargets[ i ].name = json.morphTargets[ i ].name;
+ geometry.morphTargets[ i ].vertices = [];
+
+ var dstVertices = geometry.morphTargets[ i ].vertices;
+ var srcVertices = json.morphTargets[ i ].vertices;
+
+ for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) {
+
+ var vertex = new THREE.Vector3();
+ vertex.x = srcVertices[ v ] * scale;
+ vertex.y = srcVertices[ v + 1 ] * scale;
+ vertex.z = srcVertices[ v + 2 ] * scale;
+
+ dstVertices.push( vertex );
+
+ }
+
+ }
+
+ }
+
+ if ( json.morphColors !== undefined && json.morphColors.length > 0 ) {
+
+ console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' );
+
+ var faces = geometry.faces;
+ var morphColors = json.morphColors[ 0 ].colors;
+
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
+
+ faces[ i ].color.fromArray( morphColors, i * 3 );
+
+ }
+
+ }
+
+ }
+
+ function parseAnimations() {
+
+ var outputAnimations = [];
+
+ // parse old style Bone/Hierarchy animations
+ var animations = [];
+ if ( json.animation !== undefined ) {
+ animations.push( json.animation );
+ }
+ if ( json.animations !== undefined ) {
+ if ( json.animations.length ) {
+ animations = animations.concat( json.animations );
+ } else {
+ animations.push( json.animations );
+ }
+ }
+
+ for ( var i = 0; i < animations.length; i ++ ) {
+
+ var clip = THREE.AnimationClip.parseAnimation( animations[i], geometry.bones );
+ if ( clip ) outputAnimations.push( clip );
+
+ }
+
+ // parse implicit morph animations
+ if ( geometry.morphTargets ) {
+
+ // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary.
+ var morphAnimationClips = THREE.AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 );
+ outputAnimations = outputAnimations.concat( morphAnimationClips );
+
+ }
+
+ if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations;
+
+ };
+
+ if ( json.materials === undefined || json.materials.length === 0 ) {
+
+ return { geometry: geometry };
+
+ } else {
+
+ var materials = THREE.Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin );
+
+ return { geometry: geometry, materials: materials };
+
+ }
+
+ }
+
+};
+
+// File:src/loaders/LoadingManager.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.LoadingManager = function ( onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var isLoading = false, itemsLoaded = 0, itemsTotal = 0;
+
+ this.onStart = undefined;
+ this.onLoad = onLoad;
+ this.onProgress = onProgress;
+ this.onError = onError;
+
+ this.itemStart = function ( url ) {
+
+ itemsTotal ++;
+
+ if ( isLoading === false ) {
+
+ if ( scope.onStart !== undefined ) {
+
+ scope.onStart( url, itemsLoaded, itemsTotal );
+
+ }
+
+ }
+
+ isLoading = true;
+
+ };
+
+ this.itemEnd = function ( url ) {
+
+ itemsLoaded ++;
+
+ if ( scope.onProgress !== undefined ) {
+
+ scope.onProgress( url, itemsLoaded, itemsTotal );
+
+ }
+
+ if ( itemsLoaded === itemsTotal ) {
+
+ isLoading = false;
+
+ if ( scope.onLoad !== undefined ) {
+
+ scope.onLoad();
+
+ }
+
+ }
+
+ };
+
+ this.itemError = function ( url ) {
+
+ if ( scope.onError !== undefined ) {
+
+ scope.onError( url );
+
+ }
+
+ };
+
+};
+
+THREE.DefaultLoadingManager = new THREE.LoadingManager();
+
+// File:src/loaders/BufferGeometryLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.BufferGeometryLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.BufferGeometryLoader.prototype = {
+
+ constructor: THREE.BufferGeometryLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var loader = new THREE.XHRLoader( scope.manager );
+ loader.setCrossOrigin( this.crossOrigin );
+ loader.load( url, function ( text ) {
+
+ onLoad( scope.parse( JSON.parse( text ) ) );
+
+ }, onProgress, onError );
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ },
+
+ parse: function ( json ) {
+
+ var geometry = new THREE.BufferGeometry();
+
+ var index = json.data.index;
+
+ if ( index !== undefined ) {
+
+ var typedArray = new self[ index.type ]( index.array );
+ geometry.setIndex( new THREE.BufferAttribute( typedArray, 1 ) );
+
+ }
+
+ var attributes = json.data.attributes;
+
+ for ( var key in attributes ) {
+
+ var attribute = attributes[ key ];
+ var typedArray = new self[ attribute.type ]( attribute.array );
+
+ geometry.addAttribute( key, new THREE.BufferAttribute( typedArray, attribute.itemSize ) );
+
+ }
+
+ var groups = json.data.groups || json.data.drawcalls || json.data.offsets;
+
+ if ( groups !== undefined ) {
+
+ for ( var i = 0, n = groups.length; i !== n; ++ i ) {
+
+ var group = groups[ i ];
+
+ geometry.addGroup( group.start, group.count );
+
+ }
+
+ }
+
+ var boundingSphere = json.data.boundingSphere;
+
+ if ( boundingSphere !== undefined ) {
+
+ var center = new THREE.Vector3();
+
+ if ( boundingSphere.center !== undefined ) {
+
+ center.fromArray( boundingSphere.center );
+
+ }
+
+ geometry.boundingSphere = new THREE.Sphere( center, boundingSphere.radius );
+
+ }
+
+ return geometry;
+
+ }
+
+};
+
+// File:src/loaders/MaterialLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.MaterialLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+ this.textures = {};
+
+};
+
+THREE.MaterialLoader.prototype = {
+
+ constructor: THREE.MaterialLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var loader = new THREE.XHRLoader( scope.manager );
+ loader.setCrossOrigin( this.crossOrigin );
+ loader.load( url, function ( text ) {
+
+ onLoad( scope.parse( JSON.parse( text ) ) );
+
+ }, onProgress, onError );
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ },
+
+ setTextures: function ( value ) {
+
+ this.textures = value;
+
+ },
+
+ getTexture: function ( name ) {
+
+ var textures = this.textures;
+
+ if ( textures[ name ] === undefined ) {
+
+ console.warn( 'THREE.MaterialLoader: Undefined texture', name );
+
+ }
+
+ return textures[ name ];
+
+ },
+
+ parse: function ( json ) {
+
+ var material = new THREE[ json.type ];
+ material.uuid = json.uuid;
+
+ if ( json.name !== undefined ) material.name = json.name;
+ if ( json.color !== undefined ) material.color.setHex( json.color );
+ if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive );
+ if ( json.specular !== undefined ) material.specular.setHex( json.specular );
+ if ( json.shininess !== undefined ) material.shininess = json.shininess;
+ if ( json.uniforms !== undefined ) material.uniforms = json.uniforms;
+ if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader;
+ if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader;
+ if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors;
+ if ( json.shading !== undefined ) material.shading = json.shading;
+ if ( json.blending !== undefined ) material.blending = json.blending;
+ if ( json.side !== undefined ) material.side = json.side;
+ if ( json.opacity !== undefined ) material.opacity = json.opacity;
+ if ( json.transparent !== undefined ) material.transparent = json.transparent;
+ if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest;
+ if ( json.depthTest !== undefined ) material.depthTest = json.depthTest;
+ if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite;
+ if ( json.wireframe !== undefined ) material.wireframe = json.wireframe;
+ if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth;
+
+ // for PointsMaterial
+ if ( json.size !== undefined ) material.size = json.size;
+ if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation;
+
+ // maps
+
+ if ( json.map !== undefined ) material.map = this.getTexture( json.map );
+
+ if ( json.alphaMap !== undefined ) {
+
+ material.alphaMap = this.getTexture( json.alphaMap );
+ material.transparent = true;
+
+ }
+
+ if ( json.bumpMap !== undefined ) material.bumpMap = this.getTexture( json.bumpMap );
+ if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale;
+
+ if ( json.normalMap !== undefined ) material.normalMap = this.getTexture( json.normalMap );
+ if ( json.normalScale ) material.normalScale = new THREE.Vector2( json.normalScale, json.normalScale );
+
+ if ( json.displacementMap !== undefined ) material.displacementMap = this.getTexture( json.displacementMap );
+ if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale;
+ if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias;
+
+ if ( json.specularMap !== undefined ) material.specularMap = this.getTexture( json.specularMap );
+
+ if ( json.envMap !== undefined ) {
+
+ material.envMap = this.getTexture( json.envMap );
+ material.combine = THREE.MultiplyOperation;
+
+ }
+
+ if ( json.reflectivity ) material.reflectivity = json.reflectivity;
+
+ if ( json.lightMap !== undefined ) material.lightMap = this.getTexture( json.lightMap );
+ if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity;
+
+ if ( json.aoMap !== undefined ) material.aoMap = this.getTexture( json.aoMap );
+ if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity;
+
+ // MeshFaceMaterial
+
+ if ( json.materials !== undefined ) {
+
+ for ( var i = 0, l = json.materials.length; i < l; i ++ ) {
+
+ material.materials.push( this.parse( json.materials[ i ] ) );
+
+ }
+
+ }
+
+ return material;
+
+ }
+
+};
+
+// File:src/loaders/ObjectLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.ObjectLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+ this.texturePath = '';
+
+};
+
+THREE.ObjectLoader.prototype = {
+
+ constructor: THREE.ObjectLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ if ( this.texturePath === '' ) {
+
+ this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 );
+
+ }
+
+ var scope = this;
+
+ var loader = new THREE.XHRLoader( scope.manager );
+ loader.setCrossOrigin( this.crossOrigin );
+ loader.load( url, function ( text ) {
+
+ scope.parse( JSON.parse( text ), onLoad );
+
+ }, onProgress, onError );
+
+ },
+
+ setTexturePath: function ( value ) {
+
+ this.texturePath = value;
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ },
+
+ parse: function ( json, onLoad ) {
+
+ var geometries = this.parseGeometries( json.geometries );
+
+ var images = this.parseImages( json.images, function () {
+
+ if ( onLoad !== undefined ) onLoad( object );
+
+ } );
+
+ var textures = this.parseTextures( json.textures, images );
+ var materials = this.parseMaterials( json.materials, textures );
+
+ var object = this.parseObject( json.object, geometries, materials );
+
+ if ( json.animations ) {
+
+ object.animations = this.parseAnimations( json.animations );
+
+ }
+
+ if ( json.images === undefined || json.images.length === 0 ) {
+
+ if ( onLoad !== undefined ) onLoad( object );
+
+ }
+
+ return object;
+
+ },
+
+ parseGeometries: function ( json ) {
+
+ var geometries = {};
+
+ if ( json !== undefined ) {
+
+ var geometryLoader = new THREE.JSONLoader();
+ var bufferGeometryLoader = new THREE.BufferGeometryLoader();
+
+ for ( var i = 0, l = json.length; i < l; i ++ ) {
+
+ var geometry;
+ var data = json[ i ];
+
+ switch ( data.type ) {
+
+ case 'PlaneGeometry':
+ case 'PlaneBufferGeometry':
+
+ geometry = new THREE[ data.type ](
+ data.width,
+ data.height,
+ data.widthSegments,
+ data.heightSegments
+ );
+
+ break;
+
+ case 'BoxGeometry':
+ case 'CubeGeometry': // backwards compatible
+
+ geometry = new THREE.BoxGeometry(
+ data.width,
+ data.height,
+ data.depth,
+ data.widthSegments,
+ data.heightSegments,
+ data.depthSegments
+ );
+
+ break;
+
+ case 'CircleBufferGeometry':
+
+ geometry = new THREE.CircleBufferGeometry(
+ data.radius,
+ data.segments,
+ data.thetaStart,
+ data.thetaLength
+ );
+
+ break;
+
+ case 'CircleGeometry':
+
+ geometry = new THREE.CircleGeometry(
+ data.radius,
+ data.segments,
+ data.thetaStart,
+ data.thetaLength
+ );
+
+ break;
+
+ case 'CylinderGeometry':
+
+ geometry = new THREE.CylinderGeometry(
+ data.radiusTop,
+ data.radiusBottom,
+ data.height,
+ data.radialSegments,
+ data.heightSegments,
+ data.openEnded,
+ data.thetaStart,
+ data.thetaLength
+ );
+
+ break;
+
+ case 'SphereGeometry':
+
+ geometry = new THREE.SphereGeometry(
+ data.radius,
+ data.widthSegments,
+ data.heightSegments,
+ data.phiStart,
+ data.phiLength,
+ data.thetaStart,
+ data.thetaLength
+ );
+
+ break;
+
+ case 'SphereBufferGeometry':
+
+ geometry = new THREE.SphereBufferGeometry(
+ data.radius,
+ data.widthSegments,
+ data.heightSegments,
+ data.phiStart,
+ data.phiLength,
+ data.thetaStart,
+ data.thetaLength
+ );
+
+ break;
+
+ case 'DodecahedronGeometry':
+
+ geometry = new THREE.DodecahedronGeometry(
+ data.radius,
+ data.detail
+ );
+
+ break;
+
+ case 'IcosahedronGeometry':
+
+ geometry = new THREE.IcosahedronGeometry(
+ data.radius,
+ data.detail
+ );
+
+ break;
+
+ case 'OctahedronGeometry':
+
+ geometry = new THREE.OctahedronGeometry(
+ data.radius,
+ data.detail
+ );
+
+ break;
+
+ case 'TetrahedronGeometry':
+
+ geometry = new THREE.TetrahedronGeometry(
+ data.radius,
+ data.detail
+ );
+
+ break;
+
+ case 'RingGeometry':
+
+ geometry = new THREE.RingGeometry(
+ data.innerRadius,
+ data.outerRadius,
+ data.thetaSegments,
+ data.phiSegments,
+ data.thetaStart,
+ data.thetaLength
+ );
+
+ break;
+
+ case 'TorusGeometry':
+
+ geometry = new THREE.TorusGeometry(
+ data.radius,
+ data.tube,
+ data.radialSegments,
+ data.tubularSegments,
+ data.arc
+ );
+
+ break;
+
+ case 'TorusKnotGeometry':
+
+ geometry = new THREE.TorusKnotGeometry(
+ data.radius,
+ data.tube,
+ data.radialSegments,
+ data.tubularSegments,
+ data.p,
+ data.q,
+ data.heightScale
+ );
+
+ break;
+
+ case 'BufferGeometry':
+
+ geometry = bufferGeometryLoader.parse( data );
+
+ break;
+
+ case 'Geometry':
+
+ geometry = geometryLoader.parse( data.data, this.texturePath ).geometry;
+
+ break;
+
+ default:
+
+ console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' );
+
+ continue;
+
+ }
+
+ geometry.uuid = data.uuid;
+
+ if ( data.name !== undefined ) geometry.name = data.name;
+
+ geometries[ data.uuid ] = geometry;
+
+ }
+
+ }
+
+ return geometries;
+
+ },
+
+ parseMaterials: function ( json, textures ) {
+
+ var materials = {};
+
+ if ( json !== undefined ) {
+
+ var loader = new THREE.MaterialLoader();
+ loader.setTextures( textures );
+
+ for ( var i = 0, l = json.length; i < l; i ++ ) {
+
+ var material = loader.parse( json[ i ] );
+ materials[ material.uuid ] = material;
+
+ }
+
+ }
+
+ return materials;
+
+ },
+
+ parseAnimations: function ( json ) {
+
+ var animations = [];
+
+ for ( var i = 0; i < json.length; i ++ ) {
+
+ var clip = THREE.AnimationClip.parse( json[i] );
+
+ animations.push( clip );
+
+ }
+
+ return animations;
+
+ },
+
+ parseImages: function ( json, onLoad ) {
+
+ var scope = this;
+ var images = {};
+
+ function loadImage( url ) {
+
+ scope.manager.itemStart( url );
+
+ return loader.load( url, function () {
+
+ scope.manager.itemEnd( url );
+
+ } );
+
+ }
+
+ if ( json !== undefined && json.length > 0 ) {
+
+ var manager = new THREE.LoadingManager( onLoad );
+
+ var loader = new THREE.ImageLoader( manager );
+ loader.setCrossOrigin( this.crossOrigin );
+
+ for ( var i = 0, l = json.length; i < l; i ++ ) {
+
+ var image = json[ i ];
+ var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url;
+
+ images[ image.uuid ] = loadImage( path );
+
+ }
+
+ }
+
+ return images;
+
+ },
+
+ parseTextures: function ( json, images ) {
+
+ function parseConstant( value ) {
+
+ if ( typeof( value ) === 'number' ) return value;
+
+ console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value );
+
+ return THREE[ value ];
+
+ }
+
+ var textures = {};
+
+ if ( json !== undefined ) {
+
+ for ( var i = 0, l = json.length; i < l; i ++ ) {
+
+ var data = json[ i ];
+
+ if ( data.image === undefined ) {
+
+ console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid );
+
+ }
+
+ if ( images[ data.image ] === undefined ) {
+
+ console.warn( 'THREE.ObjectLoader: Undefined image', data.image );
+
+ }
+
+ var texture = new THREE.Texture( images[ data.image ] );
+ texture.needsUpdate = true;
+
+ texture.uuid = data.uuid;
+
+ if ( data.name !== undefined ) texture.name = data.name;
+ if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping );
+ if ( data.offset !== undefined ) texture.offset = new THREE.Vector2( data.offset[ 0 ], data.offset[ 1 ] );
+ if ( data.repeat !== undefined ) texture.repeat = new THREE.Vector2( data.repeat[ 0 ], data.repeat[ 1 ] );
+ if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter );
+ if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter );
+ if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy;
+ if ( Array.isArray( data.wrap ) ) {
+
+ texture.wrapS = parseConstant( data.wrap[ 0 ] );
+ texture.wrapT = parseConstant( data.wrap[ 1 ] );
+
+ }
+
+ textures[ data.uuid ] = texture;
+
+ }
+
+ }
+
+ return textures;
+
+ },
+
+ parseObject: function () {
+
+ var matrix = new THREE.Matrix4();
+
+ return function ( data, geometries, materials ) {
+
+ var object;
+
+ function getGeometry( name ) {
+
+ if ( geometries[ name ] === undefined ) {
+
+ console.warn( 'THREE.ObjectLoader: Undefined geometry', name );
+
+ }
+
+ return geometries[ name ];
+
+ }
+
+ function getMaterial( name ) {
+
+ if ( name === undefined ) return undefined;
+
+ if ( materials[ name ] === undefined ) {
+
+ console.warn( 'THREE.ObjectLoader: Undefined material', name );
+
+ }
+
+ return materials[ name ];
+
+ }
+
+ switch ( data.type ) {
+
+ case 'Scene':
+
+ object = new THREE.Scene();
+
+ break;
+
+ case 'PerspectiveCamera':
+
+ object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far );
+
+ break;
+
+ case 'OrthographicCamera':
+
+ object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far );
+
+ break;
+
+ case 'AmbientLight':
+
+ object = new THREE.AmbientLight( data.color );
+
+ break;
+
+ case 'DirectionalLight':
+
+ object = new THREE.DirectionalLight( data.color, data.intensity );
+
+ break;
+
+ case 'PointLight':
+
+ object = new THREE.PointLight( data.color, data.intensity, data.distance, data.decay );
+
+ break;
+
+ case 'SpotLight':
+
+ object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent, data.decay );
+
+ break;
+
+ case 'HemisphereLight':
+
+ object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity );
+
+ break;
+
+ case 'Mesh':
+
+ object = new THREE.Mesh( getGeometry( data.geometry ), getMaterial( data.material ) );
+
+ break;
+
+ case 'LOD':
+
+ object = new THREE.LOD();
+
+ break;
+
+ case 'Line':
+
+ object = new THREE.Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode );
+
+ break;
+
+ case 'PointCloud':
+ case 'Points':
+
+ object = new THREE.Points( getGeometry( data.geometry ), getMaterial( data.material ) );
+
+ break;
+
+ case 'Sprite':
+
+ object = new THREE.Sprite( getMaterial( data.material ) );
+
+ break;
+
+ case 'Group':
+
+ object = new THREE.Group();
+
+ break;
+
+ default:
+
+ object = new THREE.Object3D();
+
+ }
+
+ object.uuid = data.uuid;
+
+ if ( data.name !== undefined ) object.name = data.name;
+ if ( data.matrix !== undefined ) {
+
+ matrix.fromArray( data.matrix );
+ matrix.decompose( object.position, object.quaternion, object.scale );
+
+ } else {
+
+ if ( data.position !== undefined ) object.position.fromArray( data.position );
+ if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation );
+ if ( data.scale !== undefined ) object.scale.fromArray( data.scale );
+
+ }
+
+ if ( data.castShadow !== undefined ) object.castShadow = data.castShadow;
+ if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow;
+
+ if ( data.visible !== undefined ) object.visible = data.visible;
+ if ( data.userData !== undefined ) object.userData = data.userData;
+
+ if ( data.children !== undefined ) {
+
+ for ( var child in data.children ) {
+
+ object.add( this.parseObject( data.children[ child ], geometries, materials ) );
+
+ }
+
+ }
+
+ if ( data.type === 'LOD' ) {
+
+ var levels = data.levels;
+
+ for ( var l = 0; l < levels.length; l ++ ) {
+
+ var level = levels[ l ];
+ var child = object.getObjectByProperty( 'uuid', level.object );
+
+ if ( child !== undefined ) {
+
+ object.addLevel( child, level.distance );
+
+ }
+
+ }
+
+ }
+
+ return object;
+
+ }
+
+ }()
+
+};
+
+// File:src/loaders/TextureLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.TextureLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.TextureLoader.prototype = {
+
+ constructor: THREE.TextureLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var texture = new THREE.Texture();
+
+ var loader = new THREE.ImageLoader( this.manager );
+ loader.setCrossOrigin( this.crossOrigin );
+ loader.load( url, function ( image ) {
+
+ texture.image = image;
+ texture.needsUpdate = true;
+
+ if ( onLoad !== undefined ) {
+
+ onLoad( texture );
+
+ }
+
+ }, onProgress, onError );
+
+ return texture;
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ }
+
+};
+
+// File:src/loaders/CubeTextureLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.CubeTextureLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.CubeTextureLoader.prototype = {
+
+ constructor: THREE.CubeTextureLoader,
+
+ load: function ( urls, onLoad, onProgress, onError ) {
+
+ var texture = new THREE.CubeTexture( [] );
+
+ var loader = new THREE.ImageLoader();
+ loader.setCrossOrigin( this.crossOrigin );
+
+ var loaded = 0;
+
+ function loadTexture( i ) {
+
+ loader.load( urls[ i ], function ( image ) {
+
+ texture.images[ i ] = image;
+
+ loaded ++;
+
+ if ( loaded === 6 ) {
+
+ texture.needsUpdate = true;
+
+ if ( onLoad ) onLoad( texture );
+
+ }
+
+ }, undefined, onError );
+
+ }
+
+ for ( var i = 0; i < urls.length; ++ i ) {
+
+ loadTexture( i );
+
+ }
+
+ return texture;
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ }
+
+};
+
+// File:src/loaders/BinaryTextureLoader.js
+
+/**
+ * @author Nikos M. / https://github.com/foo123/
+ *
+ * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...)
+ */
+
+THREE.DataTextureLoader = THREE.BinaryTextureLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+ // override in sub classes
+ this._parser = null;
+
+};
+
+THREE.BinaryTextureLoader.prototype = {
+
+ constructor: THREE.BinaryTextureLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var texture = new THREE.DataTexture();
+
+ var loader = new THREE.XHRLoader( this.manager );
+ loader.setCrossOrigin( this.crossOrigin );
+ loader.setResponseType( 'arraybuffer' );
+
+ loader.load( url, function ( buffer ) {
+
+ var texData = scope._parser( buffer );
+
+ if ( ! texData ) return;
+
+ if ( undefined !== texData.image ) {
+
+ texture.image = texData.image;
+
+ } else if ( undefined !== texData.data ) {
+
+ texture.image.width = texData.width;
+ texture.image.height = texData.height;
+ texture.image.data = texData.data;
+
+ }
+
+ texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : THREE.ClampToEdgeWrapping;
+ texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : THREE.ClampToEdgeWrapping;
+
+ texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : THREE.LinearFilter;
+ texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : THREE.LinearMipMapLinearFilter;
+
+ texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1;
+
+ if ( undefined !== texData.format ) {
+
+ texture.format = texData.format;
+
+ }
+ if ( undefined !== texData.type ) {
+
+ texture.type = texData.type;
+
+ }
+
+ if ( undefined !== texData.mipmaps ) {
+
+ texture.mipmaps = texData.mipmaps;
+
+ }
+
+ if ( 1 === texData.mipmapCount ) {
+
+ texture.minFilter = THREE.LinearFilter;
+
+ }
+
+ texture.needsUpdate = true;
+
+ if ( onLoad ) onLoad( texture, texData );
+
+ }, onProgress, onError );
+
+
+ return texture;
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ }
+
+};
+
+// File:src/loaders/CompressedTextureLoader.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ *
+ * Abstract Base class to block based textures loader (dds, pvr, ...)
+ */
+
+THREE.CompressedTextureLoader = function ( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+ // override in sub classes
+ this._parser = null;
+
+};
+
+
+THREE.CompressedTextureLoader.prototype = {
+
+ constructor: THREE.CompressedTextureLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var images = [];
+
+ var texture = new THREE.CompressedTexture();
+ texture.image = images;
+
+ var loader = new THREE.XHRLoader( this.manager );
+ loader.setCrossOrigin( this.crossOrigin );
+ loader.setResponseType( 'arraybuffer' );
+
+ if ( Array.isArray( url ) ) {
+
+ var loaded = 0;
+
+ var loadTexture = function ( i ) {
+
+ loader.load( url[ i ], function ( buffer ) {
+
+ var texDatas = scope._parser( buffer, true );
+
+ images[ i ] = {
+ width: texDatas.width,
+ height: texDatas.height,
+ format: texDatas.format,
+ mipmaps: texDatas.mipmaps
+ };
+
+ loaded += 1;
+
+ if ( loaded === 6 ) {
+
+ if ( texDatas.mipmapCount === 1 )
+ texture.minFilter = THREE.LinearFilter;
+
+ texture.format = texDatas.format;
+ texture.needsUpdate = true;
+
+ if ( onLoad ) onLoad( texture );
+
+ }
+
+ }, onProgress, onError );
+
+ };
+
+ for ( var i = 0, il = url.length; i < il; ++ i ) {
+
+ loadTexture( i );
+
+ }
+
+ } else {
+
+ // compressed cubemap texture stored in a single DDS file
+
+ loader.load( url, function ( buffer ) {
+
+ var texDatas = scope._parser( buffer, true );
+
+ if ( texDatas.isCubemap ) {
+
+ var faces = texDatas.mipmaps.length / texDatas.mipmapCount;
+
+ for ( var f = 0; f < faces; f ++ ) {
+
+ images[ f ] = { mipmaps : [] };
+
+ for ( var i = 0; i < texDatas.mipmapCount; i ++ ) {
+
+ images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] );
+ images[ f ].format = texDatas.format;
+ images[ f ].width = texDatas.width;
+ images[ f ].height = texDatas.height;
+
+ }
+
+ }
+
+ } else {
+
+ texture.image.width = texDatas.width;
+ texture.image.height = texDatas.height;
+ texture.mipmaps = texDatas.mipmaps;
+
+ }
+
+ if ( texDatas.mipmapCount === 1 ) {
+
+ texture.minFilter = THREE.LinearFilter;
+
+ }
+
+ texture.format = texDatas.format;
+ texture.needsUpdate = true;
+
+ if ( onLoad ) onLoad( texture );
+
+ }, onProgress, onError );
+
+ }
+
+ return texture;
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+
+ }
+
+};
+
+// File:src/materials/Material.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Material = function () {
+
+ Object.defineProperty( this, 'id', { value: THREE.MaterialIdCount ++ } );
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.name = '';
+ this.type = 'Material';
+
+ this.side = THREE.FrontSide;
+
+ this.opacity = 1;
+ this.transparent = false;
+
+ this.blending = THREE.NormalBlending;
+
+ this.blendSrc = THREE.SrcAlphaFactor;
+ this.blendDst = THREE.OneMinusSrcAlphaFactor;
+ this.blendEquation = THREE.AddEquation;
+ this.blendSrcAlpha = null;
+ this.blendDstAlpha = null;
+ this.blendEquationAlpha = null;
+
+ this.depthFunc = THREE.LessEqualDepth;
+ this.depthTest = true;
+ this.depthWrite = true;
+
+ this.colorWrite = true;
+
+ this.precision = null; // override the renderer's default precision for this material
+
+ this.polygonOffset = false;
+ this.polygonOffsetFactor = 0;
+ this.polygonOffsetUnits = 0;
+
+ this.alphaTest = 0;
+
+ this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer
+
+ this.visible = true;
+
+ this._needsUpdate = true;
+
+};
+
+THREE.Material.prototype = {
+
+ constructor: THREE.Material,
+
+ get needsUpdate () {
+
+ return this._needsUpdate;
+
+ },
+
+ set needsUpdate ( value ) {
+
+ if ( value === true ) this.update();
+
+ this._needsUpdate = value;
+
+ },
+
+ setValues: function ( values ) {
+
+ if ( values === undefined ) return;
+
+ for ( var key in values ) {
+
+ var newValue = values[ key ];
+
+ if ( newValue === undefined ) {
+
+ console.warn( "THREE.Material: '" + key + "' parameter is undefined." );
+ continue;
+
+ }
+
+ var currentValue = this[ key ];
+
+ if ( currentValue === undefined ) {
+
+ console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." );
+ continue;
+
+ }
+
+ if ( currentValue instanceof THREE.Color ) {
+
+ currentValue.set( newValue );
+
+ } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) {
+
+ currentValue.copy( newValue );
+
+ } else if ( key === 'overdraw' ) {
+
+ // ensure overdraw is backwards-compatible with legacy boolean type
+ this[ key ] = Number( newValue );
+
+ } else {
+
+ this[ key ] = newValue;
+
+ }
+
+ }
+
+ },
+
+ toJSON: function ( meta ) {
+
+ var data = {
+ metadata: {
+ version: 4.4,
+ type: 'Material',
+ generator: 'Material.toJSON'
+ }
+ };
+
+ // standard Material serialization
+ data.uuid = this.uuid;
+ data.type = this.type;
+ if ( this.name !== '' ) data.name = this.name;
+
+ if ( this.color instanceof THREE.Color ) data.color = this.color.getHex();
+ if ( this.emissive instanceof THREE.Color ) data.emissive = this.emissive.getHex();
+ if ( this.specular instanceof THREE.Color ) data.specular = this.specular.getHex();
+ if ( this.shininess !== undefined ) data.shininess = this.shininess;
+
+ if ( this.map instanceof THREE.Texture ) data.map = this.map.toJSON( meta ).uuid;
+ if ( this.alphaMap instanceof THREE.Texture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid;
+ if ( this.lightMap instanceof THREE.Texture ) data.lightMap = this.lightMap.toJSON( meta ).uuid;
+ if ( this.bumpMap instanceof THREE.Texture ) {
+
+ data.bumpMap = this.bumpMap.toJSON( meta ).uuid;
+ data.bumpScale = this.bumpScale;
+
+ }
+ if ( this.normalMap instanceof THREE.Texture ) {
+
+ data.normalMap = this.normalMap.toJSON( meta ).uuid;
+ data.normalScale = this.normalScale; // Removed for now, causes issue in editor ui.js
+
+ }
+ if ( this.displacementMap instanceof THREE.Texture ) {
+
+ data.displacementMap = this.displacementMap.toJSON( meta ).uuid;
+ data.displacementScale = this.displacementScale;
+ data.displacementBias = this.displacementBias;
+
+ }
+ if ( this.specularMap instanceof THREE.Texture ) data.specularMap = this.specularMap.toJSON( meta ).uuid;
+ if ( this.envMap instanceof THREE.Texture ) {
+
+ data.envMap = this.envMap.toJSON( meta ).uuid;
+ data.reflectivity = this.reflectivity; // Scale behind envMap
+
+ }
+
+ if ( this.size !== undefined ) data.size = this.size;
+ if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;
+
+ if ( this.vertexColors !== undefined && this.vertexColors !== THREE.NoColors ) data.vertexColors = this.vertexColors;
+ if ( this.shading !== undefined && this.shading !== THREE.SmoothShading ) data.shading = this.shading;
+ if ( this.blending !== undefined && this.blending !== THREE.NormalBlending ) data.blending = this.blending;
+ if ( this.side !== undefined && this.side !== THREE.FrontSide ) data.side = this.side;
+
+ if ( this.opacity < 1 ) data.opacity = this.opacity;
+ if ( this.transparent === true ) data.transparent = this.transparent;
+ if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;
+ if ( this.wireframe === true ) data.wireframe = this.wireframe;
+ if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;
+
+ return data;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.name = source.name;
+
+ this.side = source.side;
+
+ this.opacity = source.opacity;
+ this.transparent = source.transparent;
+
+ this.blending = source.blending;
+
+ this.blendSrc = source.blendSrc;
+ this.blendDst = source.blendDst;
+ this.blendEquation = source.blendEquation;
+ this.blendSrcAlpha = source.blendSrcAlpha;
+ this.blendDstAlpha = source.blendDstAlpha;
+ this.blendEquationAlpha = source.blendEquationAlpha;
+
+ this.depthFunc = source.depthFunc;
+ this.depthTest = source.depthTest;
+ this.depthWrite = source.depthWrite;
+
+ this.precision = source.precision;
+
+ this.polygonOffset = source.polygonOffset;
+ this.polygonOffsetFactor = source.polygonOffsetFactor;
+ this.polygonOffsetUnits = source.polygonOffsetUnits;
+
+ this.alphaTest = source.alphaTest;
+
+ this.overdraw = source.overdraw;
+
+ this.visible = source.visible;
+
+ return this;
+
+ },
+
+ update: function () {
+
+ this.dispatchEvent( { type: 'update' } );
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ },
+
+ // Deprecated
+
+ get wrapAround () {
+
+ console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' );
+
+ },
+
+ set wrapAround ( boolean ) {
+
+ console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' );
+
+ },
+
+ get wrapRGB () {
+
+ console.warn( 'THREE.' + this.type + ': .wrapRGB has been removed.' );
+ return new THREE.Color();
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.Material.prototype );
+
+THREE.MaterialIdCount = 0;
+
+// File:src/materials/LineBasicMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * color: ,
+ * opacity: ,
+ *
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * linewidth: ,
+ * linecap: "round",
+ * linejoin: "round",
+ *
+ * vertexColors:
+ *
+ * fog:
+ * }
+ */
+
+THREE.LineBasicMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'LineBasicMaterial';
+
+ this.color = new THREE.Color( 0xffffff );
+
+ this.linewidth = 1;
+ this.linecap = 'round';
+ this.linejoin = 'round';
+
+ this.vertexColors = THREE.NoColors;
+
+ this.fog = true;
+
+ this.setValues( parameters );
+
+};
+
+THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.LineBasicMaterial.prototype.constructor = THREE.LineBasicMaterial;
+
+THREE.LineBasicMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+
+ this.linewidth = source.linewidth;
+ this.linecap = source.linecap;
+ this.linejoin = source.linejoin;
+
+ this.vertexColors = source.vertexColors;
+
+ this.fog = source.fog;
+
+ return this;
+
+};
+
+// File:src/materials/LineDashedMaterial.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * color: ,
+ * opacity: ,
+ *
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * linewidth: ,
+ *
+ * scale: ,
+ * dashSize: ,
+ * gapSize: ,
+ *
+ * vertexColors:
+ *
+ * fog:
+ * }
+ */
+
+THREE.LineDashedMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'LineDashedMaterial';
+
+ this.color = new THREE.Color( 0xffffff );
+
+ this.linewidth = 1;
+
+ this.scale = 1;
+ this.dashSize = 3;
+ this.gapSize = 1;
+
+ this.vertexColors = false;
+
+ this.fog = true;
+
+ this.setValues( parameters );
+
+};
+
+THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.LineDashedMaterial.prototype.constructor = THREE.LineDashedMaterial;
+
+THREE.LineDashedMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+
+ this.linewidth = source.linewidth;
+
+ this.scale = source.scale;
+ this.dashSize = source.dashSize;
+ this.gapSize = source.gapSize;
+
+ this.vertexColors = source.vertexColors;
+
+ this.fog = source.fog;
+
+ return this;
+
+};
+
+// File:src/materials/MeshBasicMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * color: ,
+ * opacity: ,
+ * map: new THREE.Texture( ),
+ *
+ * aoMap: new THREE.Texture( ),
+ * aoMapIntensity:
+ *
+ * specularMap: new THREE.Texture( ),
+ *
+ * alphaMap: new THREE.Texture( ),
+ *
+ * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
+ * combine: THREE.Multiply,
+ * reflectivity: ,
+ * refractionRatio: ,
+ *
+ * shading: THREE.SmoothShading,
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * wireframe: ,
+ * wireframeLinewidth: ,
+ *
+ * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
+ *
+ * skinning: ,
+ * morphTargets: ,
+ *
+ * fog:
+ * }
+ */
+
+THREE.MeshBasicMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'MeshBasicMaterial';
+
+ this.color = new THREE.Color( 0xffffff ); // emissive
+
+ this.map = null;
+
+ this.aoMap = null;
+ this.aoMapIntensity = 1.0;
+
+ this.specularMap = null;
+
+ this.alphaMap = null;
+
+ this.envMap = null;
+ this.combine = THREE.MultiplyOperation;
+ this.reflectivity = 1;
+ this.refractionRatio = 0.98;
+
+ this.fog = true;
+
+ this.shading = THREE.SmoothShading;
+
+ this.wireframe = false;
+ this.wireframeLinewidth = 1;
+ this.wireframeLinecap = 'round';
+ this.wireframeLinejoin = 'round';
+
+ this.vertexColors = THREE.NoColors;
+
+ this.skinning = false;
+ this.morphTargets = false;
+
+ this.setValues( parameters );
+
+};
+
+THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshBasicMaterial.prototype.constructor = THREE.MeshBasicMaterial;
+
+THREE.MeshBasicMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+
+ this.map = source.map;
+
+ this.aoMap = source.aoMap;
+ this.aoMapIntensity = source.aoMapIntensity;
+
+ this.specularMap = source.specularMap;
+
+ this.alphaMap = source.alphaMap;
+
+ this.envMap = source.envMap;
+ this.combine = source.combine;
+ this.reflectivity = source.reflectivity;
+ this.refractionRatio = source.refractionRatio;
+
+ this.fog = source.fog;
+
+ this.shading = source.shading;
+
+ this.wireframe = source.wireframe;
+ this.wireframeLinewidth = source.wireframeLinewidth;
+ this.wireframeLinecap = source.wireframeLinecap;
+ this.wireframeLinejoin = source.wireframeLinejoin;
+
+ this.vertexColors = source.vertexColors;
+
+ this.skinning = source.skinning;
+ this.morphTargets = source.morphTargets;
+
+ return this;
+
+};
+
+// File:src/materials/MeshLambertMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * color: ,
+ * emissive: ,
+ * opacity: ,
+ *
+ * map: new THREE.Texture( ),
+ *
+ * specularMap: new THREE.Texture( ),
+ *
+ * alphaMap: new THREE.Texture( ),
+ *
+ * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
+ * combine: THREE.Multiply,
+ * reflectivity: ,
+ * refractionRatio: ,
+ *
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * wireframe: ,
+ * wireframeLinewidth: ,
+ *
+ * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
+ *
+ * skinning: ,
+ * morphTargets: ,
+ * morphNormals: ,
+ *
+ * fog:
+ * }
+ */
+
+THREE.MeshLambertMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'MeshLambertMaterial';
+
+ this.color = new THREE.Color( 0xffffff ); // diffuse
+ this.emissive = new THREE.Color( 0x000000 );
+
+ this.map = null;
+
+ this.specularMap = null;
+
+ this.alphaMap = null;
+
+ this.envMap = null;
+ this.combine = THREE.MultiplyOperation;
+ this.reflectivity = 1;
+ this.refractionRatio = 0.98;
+
+ this.fog = true;
+
+ this.wireframe = false;
+ this.wireframeLinewidth = 1;
+ this.wireframeLinecap = 'round';
+ this.wireframeLinejoin = 'round';
+
+ this.vertexColors = THREE.NoColors;
+
+ this.skinning = false;
+ this.morphTargets = false;
+ this.morphNormals = false;
+
+ this.setValues( parameters );
+
+};
+
+THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshLambertMaterial.prototype.constructor = THREE.MeshLambertMaterial;
+
+THREE.MeshLambertMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+ this.emissive.copy( source.emissive );
+
+ this.map = source.map;
+
+ this.specularMap = source.specularMap;
+
+ this.alphaMap = source.alphaMap;
+
+ this.envMap = source.envMap;
+ this.combine = source.combine;
+ this.reflectivity = source.reflectivity;
+ this.refractionRatio = source.refractionRatio;
+
+ this.fog = source.fog;
+
+ this.wireframe = source.wireframe;
+ this.wireframeLinewidth = source.wireframeLinewidth;
+ this.wireframeLinecap = source.wireframeLinecap;
+ this.wireframeLinejoin = source.wireframeLinejoin;
+
+ this.vertexColors = source.vertexColors;
+
+ this.skinning = source.skinning;
+ this.morphTargets = source.morphTargets;
+ this.morphNormals = source.morphNormals;
+
+ return this;
+
+};
+
+// File:src/materials/MeshPhongMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * color: ,
+ * emissive: ,
+ * specular: ,
+ * shininess: ,
+ * opacity: ,
+ *
+ * map: new THREE.Texture( ),
+ *
+ * lightMap: new THREE.Texture( ),
+ * lightMapIntensity:
+ *
+ * aoMap: new THREE.Texture( ),
+ * aoMapIntensity:
+ *
+ * emissiveMap: new THREE.Texture( ),
+ *
+ * bumpMap: new THREE.Texture( ),
+ * bumpScale: ,
+ *
+ * normalMap: new THREE.Texture( ),
+ * normalScale: ,
+ *
+ * displacementMap: new THREE.Texture( ),
+ * displacementScale: ,
+ * displacementBias: ,
+ *
+ * specularMap: new THREE.Texture( ),
+ *
+ * alphaMap: new THREE.Texture( ),
+ *
+ * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
+ * combine: THREE.Multiply,
+ * reflectivity: ,
+ * refractionRatio: ,
+ *
+ * shading: THREE.SmoothShading,
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * wireframe: ,
+ * wireframeLinewidth: ,
+ *
+ * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
+ *
+ * skinning: ,
+ * morphTargets: ,
+ * morphNormals: ,
+ *
+ * fog:
+ * }
+ */
+
+THREE.MeshPhongMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'MeshPhongMaterial';
+
+ this.color = new THREE.Color( 0xffffff ); // diffuse
+ this.emissive = new THREE.Color( 0x000000 );
+ this.specular = new THREE.Color( 0x111111 );
+ this.shininess = 30;
+
+ this.metal = false;
+
+ this.map = null;
+
+ this.lightMap = null;
+ this.lightMapIntensity = 1.0;
+
+ this.aoMap = null;
+ this.aoMapIntensity = 1.0;
+
+ this.emissiveMap = null;
+
+ this.bumpMap = null;
+ this.bumpScale = 1;
+
+ this.normalMap = null;
+ this.normalScale = new THREE.Vector2( 1, 1 );
+
+ this.displacementMap = null;
+ this.displacementScale = 1;
+ this.displacementBias = 0;
+
+ this.specularMap = null;
+
+ this.alphaMap = null;
+
+ this.envMap = null;
+ this.combine = THREE.MultiplyOperation;
+ this.reflectivity = 1;
+ this.refractionRatio = 0.98;
+
+ this.fog = true;
+
+ this.shading = THREE.SmoothShading;
+
+ this.wireframe = false;
+ this.wireframeLinewidth = 1;
+ this.wireframeLinecap = 'round';
+ this.wireframeLinejoin = 'round';
+
+ this.vertexColors = THREE.NoColors;
+
+ this.skinning = false;
+ this.morphTargets = false;
+ this.morphNormals = false;
+
+ this.setValues( parameters );
+
+};
+
+THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshPhongMaterial.prototype.constructor = THREE.MeshPhongMaterial;
+
+THREE.MeshPhongMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+ this.emissive.copy( source.emissive );
+ this.specular.copy( source.specular );
+ this.shininess = source.shininess;
+
+ this.metal = source.metal;
+
+ this.map = source.map;
+
+ this.lightMap = source.lightMap;
+ this.lightMapIntensity = source.lightMapIntensity;
+
+ this.aoMap = source.aoMap;
+ this.aoMapIntensity = source.aoMapIntensity;
+
+ this.emissiveMap = source.emissiveMap;
+
+ this.bumpMap = source.bumpMap;
+ this.bumpScale = source.bumpScale;
+
+ this.normalMap = source.normalMap;
+ this.normalScale.copy( source.normalScale );
+
+ this.displacementMap = source.displacementMap;
+ this.displacementScale = source.displacementScale;
+ this.displacementBias = source.displacementBias;
+
+ this.specularMap = source.specularMap;
+
+ this.alphaMap = source.alphaMap;
+
+ this.envMap = source.envMap;
+ this.combine = source.combine;
+ this.reflectivity = source.reflectivity;
+ this.refractionRatio = source.refractionRatio;
+
+ this.fog = source.fog;
+
+ this.shading = source.shading;
+
+ this.wireframe = source.wireframe;
+ this.wireframeLinewidth = source.wireframeLinewidth;
+ this.wireframeLinecap = source.wireframeLinecap;
+ this.wireframeLinejoin = source.wireframeLinejoin;
+
+ this.vertexColors = source.vertexColors;
+
+ this.skinning = source.skinning;
+ this.morphTargets = source.morphTargets;
+ this.morphNormals = source.morphNormals;
+
+ return this;
+
+};
+
+// File:src/materials/MeshDepthMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * opacity: ,
+ *
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * wireframe: ,
+ * wireframeLinewidth:
+ * }
+ */
+
+THREE.MeshDepthMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'MeshDepthMaterial';
+
+ this.morphTargets = false;
+ this.wireframe = false;
+ this.wireframeLinewidth = 1;
+
+ this.setValues( parameters );
+
+};
+
+THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshDepthMaterial.prototype.constructor = THREE.MeshDepthMaterial;
+
+THREE.MeshDepthMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.wireframe = source.wireframe;
+ this.wireframeLinewidth = source.wireframeLinewidth;
+
+ return this;
+
+};
+
+// File:src/materials/MeshNormalMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ *
+ * parameters = {
+ * opacity: ,
+ *
+ * shading: THREE.FlatShading,
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * wireframe: ,
+ * wireframeLinewidth:
+ * }
+ */
+
+THREE.MeshNormalMaterial = function ( parameters ) {
+
+ THREE.Material.call( this, parameters );
+
+ this.type = 'MeshNormalMaterial';
+
+ this.wireframe = false;
+ this.wireframeLinewidth = 1;
+
+ this.morphTargets = false;
+
+ this.setValues( parameters );
+
+};
+
+THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshNormalMaterial.prototype.constructor = THREE.MeshNormalMaterial;
+
+THREE.MeshNormalMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.wireframe = source.wireframe;
+ this.wireframeLinewidth = source.wireframeLinewidth;
+
+ return this;
+
+};
+
+// File:src/materials/MultiMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.MultiMaterial = function ( materials ) {
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.type = 'MultiMaterial';
+
+ this.materials = materials instanceof Array ? materials : [];
+
+ this.visible = true;
+
+};
+
+THREE.MultiMaterial.prototype = {
+
+ constructor: THREE.MultiMaterial,
+
+ toJSON: function () {
+
+ var output = {
+ metadata: {
+ version: 4.2,
+ type: 'material',
+ generator: 'MaterialExporter'
+ },
+ uuid: this.uuid,
+ type: this.type,
+ materials: []
+ };
+
+ for ( var i = 0, l = this.materials.length; i < l; i ++ ) {
+
+ output.materials.push( this.materials[ i ].toJSON() );
+
+ }
+
+ output.visible = this.visible;
+
+ return output;
+
+ },
+
+ clone: function () {
+
+ var material = new this.constructor();
+
+ for ( var i = 0; i < this.materials.length; i ++ ) {
+
+ material.materials.push( this.materials[ i ].clone() );
+
+ }
+
+ material.visible = this.visible;
+
+ return material;
+
+ }
+
+};
+
+// backwards compatibility
+
+THREE.MeshFaceMaterial = THREE.MultiMaterial;
+
+// File:src/materials/PointsMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * color: ,
+ * opacity: ,
+ * map: new THREE.Texture( ),
+ *
+ * size: ,
+ * sizeAttenuation: ,
+ *
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * vertexColors: ,
+ *
+ * fog:
+ * }
+ */
+
+THREE.PointsMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'PointsMaterial';
+
+ this.color = new THREE.Color( 0xffffff );
+
+ this.map = null;
+
+ this.size = 1;
+ this.sizeAttenuation = true;
+
+ this.vertexColors = THREE.NoColors;
+
+ this.fog = true;
+
+ this.setValues( parameters );
+
+};
+
+THREE.PointsMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.PointsMaterial.prototype.constructor = THREE.PointsMaterial;
+
+THREE.PointsMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+
+ this.map = source.map;
+
+ this.size = source.size;
+ this.sizeAttenuation = source.sizeAttenuation;
+
+ this.vertexColors = source.vertexColors;
+
+ this.fog = source.fog;
+
+ return this;
+
+};
+
+// backwards compatibility
+
+THREE.PointCloudMaterial = function ( parameters ) {
+
+ console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' );
+ return new THREE.PointsMaterial( parameters );
+
+};
+
+THREE.ParticleBasicMaterial = function ( parameters ) {
+
+ console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' );
+ return new THREE.PointsMaterial( parameters );
+
+};
+
+THREE.ParticleSystemMaterial = function ( parameters ) {
+
+ console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' );
+ return new THREE.PointsMaterial( parameters );
+
+};
+
+// File:src/materials/ShaderMaterial.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * defines: { "label" : "value" },
+ * uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } },
+ *
+ * fragmentShader: ,
+ * vertexShader: ,
+ *
+ * shading: THREE.SmoothShading,
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * wireframe: ,
+ * wireframeLinewidth: ,
+ *
+ * lights: ,
+ *
+ * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
+ *
+ * skinning: ,
+ * morphTargets: ,
+ * morphNormals: ,
+ *
+ * fog:
+ * }
+ */
+
+THREE.ShaderMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'ShaderMaterial';
+
+ this.defines = {};
+ this.uniforms = {};
+
+ this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}';
+ this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}';
+
+ this.shading = THREE.SmoothShading;
+
+ this.linewidth = 1;
+
+ this.wireframe = false;
+ this.wireframeLinewidth = 1;
+
+ this.fog = false; // set to use scene fog
+
+ this.lights = false; // set to use scene lights
+
+ this.vertexColors = THREE.NoColors; // set to use "color" attribute stream
+
+ this.skinning = false; // set to use skinning attribute streams
+
+ this.morphTargets = false; // set to use morph targets
+ this.morphNormals = false; // set to use morph normals
+
+ this.derivatives = false; // set to use derivatives
+
+ // When rendered geometry doesn't include these attributes but the material does,
+ // use these default values in WebGL. This avoids errors when buffer data is missing.
+ this.defaultAttributeValues = {
+ 'color': [ 1, 1, 1 ],
+ 'uv': [ 0, 0 ],
+ 'uv2': [ 0, 0 ]
+ };
+
+ this.index0AttributeName = undefined;
+
+ if ( parameters !== undefined ) {
+
+ if ( parameters.attributes !== undefined ) {
+
+ console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' );
+
+ }
+
+ this.setValues( parameters );
+
+ }
+
+};
+
+THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.ShaderMaterial.prototype.constructor = THREE.ShaderMaterial;
+
+THREE.ShaderMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.fragmentShader = source.fragmentShader;
+ this.vertexShader = source.vertexShader;
+
+ this.uniforms = THREE.UniformsUtils.clone( source.uniforms );
+
+ this.attributes = source.attributes;
+ this.defines = source.defines;
+
+ this.shading = source.shading;
+
+ this.wireframe = source.wireframe;
+ this.wireframeLinewidth = source.wireframeLinewidth;
+
+ this.fog = source.fog;
+
+ this.lights = source.lights;
+
+ this.vertexColors = source.vertexColors;
+
+ this.skinning = source.skinning;
+
+ this.morphTargets = source.morphTargets;
+ this.morphNormals = source.morphNormals;
+
+ this.derivatives = source.derivatives;
+
+ return this;
+
+};
+
+THREE.ShaderMaterial.prototype.toJSON = function ( meta ) {
+
+ var data = THREE.Material.prototype.toJSON.call( this, meta );
+
+ data.uniforms = this.uniforms;
+ data.attributes = this.attributes;
+ data.vertexShader = this.vertexShader;
+ data.fragmentShader = this.fragmentShader;
+
+ return data;
+
+};
+
+// File:src/materials/RawShaderMaterial.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.RawShaderMaterial = function ( parameters ) {
+
+ THREE.ShaderMaterial.call( this, parameters );
+
+ this.type = 'RawShaderMaterial';
+
+};
+
+THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype );
+THREE.RawShaderMaterial.prototype.constructor = THREE.RawShaderMaterial;
+// File:src/materials/SpriteMaterial.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ * color: ,
+ * opacity: ,
+ * map: new THREE.Texture( ),
+ *
+ * blending: THREE.NormalBlending,
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * uvOffset: new THREE.Vector2(),
+ * uvScale: new THREE.Vector2(),
+ *
+ * fog:
+ * }
+ */
+
+THREE.SpriteMaterial = function ( parameters ) {
+
+ THREE.Material.call( this );
+
+ this.type = 'SpriteMaterial';
+
+ this.color = new THREE.Color( 0xffffff );
+ this.map = null;
+
+ this.rotation = 0;
+
+ this.fog = false;
+
+ // set parameters
+
+ this.setValues( parameters );
+
+};
+
+THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.SpriteMaterial.prototype.constructor = THREE.SpriteMaterial;
+
+THREE.SpriteMaterial.prototype.copy = function ( source ) {
+
+ THREE.Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+ this.map = source.map;
+
+ this.rotation = source.rotation;
+
+ this.fog = source.fog;
+
+ return this;
+
+};
+
+// File:src/textures/Texture.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author szimek / https://github.com/szimek/
+ */
+
+THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
+
+ Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } );
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.name = '';
+ this.sourceFile = '';
+
+ this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE;
+ this.mipmaps = [];
+
+ this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING;
+
+ this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping;
+ this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping;
+
+ this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter;
+ this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter;
+
+ this.anisotropy = anisotropy !== undefined ? anisotropy : 1;
+
+ this.format = format !== undefined ? format : THREE.RGBAFormat;
+ this.type = type !== undefined ? type : THREE.UnsignedByteType;
+
+ this.offset = new THREE.Vector2( 0, 0 );
+ this.repeat = new THREE.Vector2( 1, 1 );
+
+ this.generateMipmaps = true;
+ this.premultiplyAlpha = false;
+ this.flipY = true;
+ this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
+
+ this.version = 0;
+ this.onUpdate = null;
+
+};
+
+THREE.Texture.DEFAULT_IMAGE = undefined;
+THREE.Texture.DEFAULT_MAPPING = THREE.UVMapping;
+
+THREE.Texture.prototype = {
+
+ constructor: THREE.Texture,
+
+ set needsUpdate ( value ) {
+
+ if ( value === true ) this.version ++;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.image = source.image;
+ this.mipmaps = source.mipmaps.slice( 0 );
+
+ this.mapping = source.mapping;
+
+ this.wrapS = source.wrapS;
+ this.wrapT = source.wrapT;
+
+ this.magFilter = source.magFilter;
+ this.minFilter = source.minFilter;
+
+ this.anisotropy = source.anisotropy;
+
+ this.format = source.format;
+ this.type = source.type;
+
+ this.offset.copy( source.offset );
+ this.repeat.copy( source.repeat );
+
+ this.generateMipmaps = source.generateMipmaps;
+ this.premultiplyAlpha = source.premultiplyAlpha;
+ this.flipY = source.flipY;
+ this.unpackAlignment = source.unpackAlignment;
+
+ return this;
+
+ },
+
+ toJSON: function ( meta ) {
+
+ if ( meta.textures[ this.uuid ] !== undefined ) {
+
+ return meta.textures[ this.uuid ];
+
+ }
+
+ function getDataURL( image ) {
+
+ var canvas;
+
+ if ( image.toDataURL !== undefined ) {
+
+ canvas = image;
+
+ } else {
+
+ canvas = document.createElement( 'canvas' );
+ canvas.width = image.width;
+ canvas.height = image.height;
+
+ canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height );
+
+ }
+
+ if ( canvas.width > 2048 || canvas.height > 2048 ) {
+
+ return canvas.toDataURL( 'image/jpeg', 0.6 );
+
+ } else {
+
+ return canvas.toDataURL( 'image/png' );
+
+ }
+
+ }
+
+ var output = {
+ metadata: {
+ version: 4.4,
+ type: 'Texture',
+ generator: 'Texture.toJSON'
+ },
+
+ uuid: this.uuid,
+ name: this.name,
+
+ mapping: this.mapping,
+
+ repeat: [ this.repeat.x, this.repeat.y ],
+ offset: [ this.offset.x, this.offset.y ],
+ wrap: [ this.wrapS, this.wrapT ],
+
+ minFilter: this.minFilter,
+ magFilter: this.magFilter,
+ anisotropy: this.anisotropy
+ };
+
+ if ( this.image !== undefined ) {
+
+ // TODO: Move to THREE.Image
+
+ var image = this.image;
+
+ if ( image.uuid === undefined ) {
+
+ image.uuid = THREE.Math.generateUUID(); // UGH
+
+ }
+
+ if ( meta.images[ image.uuid ] === undefined ) {
+
+ meta.images[ image.uuid ] = {
+ uuid: image.uuid,
+ url: getDataURL( image )
+ };
+
+ }
+
+ output.image = image.uuid;
+
+ }
+
+ meta.textures[ this.uuid ] = output;
+
+ return output;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ },
+
+ transformUv: function ( uv ) {
+
+ if ( this.mapping !== THREE.UVMapping ) return;
+
+ uv.multiply( this.repeat );
+ uv.add( this.offset );
+
+ if ( uv.x < 0 || uv.x > 1 ) {
+
+ switch ( this.wrapS ) {
+
+ case THREE.RepeatWrapping:
+
+ uv.x = uv.x - Math.floor( uv.x );
+ break;
+
+ case THREE.ClampToEdgeWrapping:
+
+ uv.x = uv.x < 0 ? 0 : 1;
+ break;
+
+ case THREE.MirroredRepeatWrapping:
+
+ if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {
+
+ uv.x = Math.ceil( uv.x ) - uv.x;
+
+ } else {
+
+ uv.x = uv.x - Math.floor( uv.x );
+
+ }
+ break;
+
+ }
+
+ }
+
+ if ( uv.y < 0 || uv.y > 1 ) {
+
+ switch ( this.wrapT ) {
+
+ case THREE.RepeatWrapping:
+
+ uv.y = uv.y - Math.floor( uv.y );
+ break;
+
+ case THREE.ClampToEdgeWrapping:
+
+ uv.y = uv.y < 0 ? 0 : 1;
+ break;
+
+ case THREE.MirroredRepeatWrapping:
+
+ if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {
+
+ uv.y = Math.ceil( uv.y ) - uv.y;
+
+ } else {
+
+ uv.y = uv.y - Math.floor( uv.y );
+
+ }
+ break;
+
+ }
+
+ }
+
+ if ( this.flipY ) {
+
+ uv.y = 1 - uv.y;
+
+ }
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype );
+
+THREE.TextureIdCount = 0;
+
+// File:src/textures/CanvasTexture.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.CanvasTexture = function ( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
+
+ THREE.Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
+
+ this.needsUpdate = true;
+
+};
+
+THREE.CanvasTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.CanvasTexture.prototype.constructor = THREE.CanvasTexture;
+
+// File:src/textures/CubeTexture.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
+
+ mapping = mapping !== undefined ? mapping : THREE.CubeReflectionMapping;
+
+ THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
+
+ this.images = images;
+ this.flipY = false;
+
+};
+
+THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.CubeTexture.prototype.constructor = THREE.CubeTexture;
+
+THREE.CubeTexture.prototype.copy = function ( source ) {
+
+ THREE.Texture.prototype.copy.call( this, source );
+
+ this.images = source.images;
+
+ return this;
+
+};
+// File:src/textures/CompressedTexture.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {
+
+ THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
+
+ this.image = { width: width, height: height };
+ this.mipmaps = mipmaps;
+
+ // no flipping for cube textures
+ // (also flipping doesn't work for compressed textures )
+
+ this.flipY = false;
+
+ // can't generate mipmaps for compressed textures
+ // mips must be embedded in DDS files
+
+ this.generateMipmaps = false;
+
+};
+
+THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.CompressedTexture.prototype.constructor = THREE.CompressedTexture;
+
+// File:src/textures/DataTexture.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {
+
+ THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
+
+ this.image = { data: data, width: width, height: height };
+
+ this.magFilter = magFilter !== undefined ? magFilter : THREE.NearestFilter;
+ this.minFilter = minFilter !== undefined ? minFilter : THREE.NearestFilter;
+
+ this.flipY = false;
+ this.generateMipmaps = false;
+
+};
+
+THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.DataTexture.prototype.constructor = THREE.DataTexture;
+
+// File:src/textures/VideoTexture.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.VideoTexture = function ( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
+
+ THREE.Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
+
+ this.generateMipmaps = false;
+
+ var scope = this;
+
+ function update() {
+
+ requestAnimationFrame( update );
+
+ if ( video.readyState === video.HAVE_ENOUGH_DATA ) {
+
+ scope.needsUpdate = true;
+
+ }
+
+ }
+
+ update();
+
+};
+
+THREE.VideoTexture.prototype = Object.create( THREE.Texture.prototype );
+THREE.VideoTexture.prototype.constructor = THREE.VideoTexture;
+
+// File:src/objects/Group.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Group = function () {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Group';
+
+};
+
+THREE.Group.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Group.prototype.constructor = THREE.Group;
+// File:src/objects/Points.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Points = function ( geometry, material ) {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Points';
+
+ this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
+ this.material = material !== undefined ? material : new THREE.PointsMaterial( { color: Math.random() * 0xffffff } );
+
+};
+
+THREE.Points.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Points.prototype.constructor = THREE.Points;
+
+THREE.Points.prototype.raycast = ( function () {
+
+ var inverseMatrix = new THREE.Matrix4();
+ var ray = new THREE.Ray();
+
+ return function raycast( raycaster, intersects ) {
+
+ var object = this;
+ var geometry = object.geometry;
+ var threshold = raycaster.params.Points.threshold;
+
+ inverseMatrix.getInverse( this.matrixWorld );
+ ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
+
+ if ( geometry.boundingBox !== null ) {
+
+ if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) {
+
+ return;
+
+ }
+
+ }
+
+ var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );
+ var localThresholdSq = localThreshold * localThreshold;
+ var position = new THREE.Vector3();
+
+ function testPoint( point, index ) {
+
+ var rayPointDistanceSq = ray.distanceSqToPoint( point );
+
+ if ( rayPointDistanceSq < localThresholdSq ) {
+
+ var intersectPoint = ray.closestPointToPoint( point );
+ intersectPoint.applyMatrix4( object.matrixWorld );
+
+ var distance = raycaster.ray.origin.distanceTo( intersectPoint );
+
+ if ( distance < raycaster.near || distance > raycaster.far ) return;
+
+ intersects.push( {
+
+ distance: distance,
+ distanceToRay: Math.sqrt( rayPointDistanceSq ),
+ point: intersectPoint.clone(),
+ index: index,
+ face: null,
+ object: object
+
+ } );
+
+ }
+
+ }
+
+ if ( geometry instanceof THREE.BufferGeometry ) {
+
+ var index = geometry.index;
+ var attributes = geometry.attributes;
+ var positions = attributes.position.array;
+
+ if ( index !== null ) {
+
+ var indices = index.array;
+
+ for ( var i = 0, il = indices.length; i < il; i ++ ) {
+
+ var a = indices[ i ];
+
+ position.fromArray( positions, a * 3 );
+
+ testPoint( position, a );
+
+ }
+
+ } else {
+
+ for ( var i = 0, l = positions.length / 3; i < l; i ++ ) {
+
+ position.fromArray( positions, i * 3 );
+
+ testPoint( position, i );
+
+ }
+
+ }
+
+ } else {
+
+ var vertices = geometry.vertices;
+
+ for ( var i = 0, l = vertices.length; i < l; i ++ ) {
+
+ testPoint( vertices[ i ], i );
+
+ }
+
+ }
+
+ };
+
+}() );
+
+THREE.Points.prototype.clone = function () {
+
+ return new this.constructor( this.geometry, this.material ).copy( this );
+
+};
+
+// Backwards compatibility
+
+THREE.PointCloud = function ( geometry, material ) {
+
+ console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' );
+ return new THREE.Points( geometry, material );
+
+};
+
+THREE.ParticleSystem = function ( geometry, material ) {
+
+ console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' );
+ return new THREE.Points( geometry, material );
+
+};
+
+// File:src/objects/Line.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Line = function ( geometry, material, mode ) {
+
+ if ( mode === 1 ) {
+
+ console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' );
+ return new THREE.LineSegments( geometry, material );
+
+ }
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Line';
+
+ this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
+ this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } );
+
+};
+
+THREE.Line.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Line.prototype.constructor = THREE.Line;
+
+THREE.Line.prototype.raycast = ( function () {
+
+ var inverseMatrix = new THREE.Matrix4();
+ var ray = new THREE.Ray();
+ var sphere = new THREE.Sphere();
+
+ return function raycast( raycaster, intersects ) {
+
+ var precision = raycaster.linePrecision;
+ var precisionSq = precision * precision;
+
+ var geometry = this.geometry;
+
+ if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
+
+ // Checking boundingSphere distance to ray
+
+ sphere.copy( geometry.boundingSphere );
+ sphere.applyMatrix4( this.matrixWorld );
+
+ if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) {
+
+ return;
+
+ }
+
+ inverseMatrix.getInverse( this.matrixWorld );
+ ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
+
+ var vStart = new THREE.Vector3();
+ var vEnd = new THREE.Vector3();
+ var interSegment = new THREE.Vector3();
+ var interRay = new THREE.Vector3();
+ var step = this instanceof THREE.LineSegments ? 2 : 1;
+
+ if ( geometry instanceof THREE.BufferGeometry ) {
+
+ var index = geometry.index;
+ var attributes = geometry.attributes;
+
+ if ( index !== null ) {
+
+ var indices = index.array;
+ var positions = attributes.position.array;
+
+ for ( var i = 0, l = indices.length - 1; i < l; i += step ) {
+
+ var a = indices[ i ];
+ var b = indices[ i + 1 ];
+
+ vStart.fromArray( positions, a * 3 );
+ vEnd.fromArray( positions, b * 3 );
+
+ var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );
+
+ if ( distSq > precisionSq ) continue;
+
+ interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation
+
+ var distance = raycaster.ray.origin.distanceTo( interRay );
+
+ if ( distance < raycaster.near || distance > raycaster.far ) continue;
+
+ intersects.push( {
+
+ distance: distance,
+ // What do we want? intersection point on the ray or on the segment??
+ // point: raycaster.ray.at( distance ),
+ point: interSegment.clone().applyMatrix4( this.matrixWorld ),
+ index: i,
+ face: null,
+ faceIndex: null,
+ object: this
+
+ } );
+
+ }
+
+ } else {
+
+ var positions = attributes.position.array;
+
+ for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) {
+
+ vStart.fromArray( positions, 3 * i );
+ vEnd.fromArray( positions, 3 * i + 3 );
+
+ var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );
+
+ if ( distSq > precisionSq ) continue;
+
+ interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation
+
+ var distance = raycaster.ray.origin.distanceTo( interRay );
+
+ if ( distance < raycaster.near || distance > raycaster.far ) continue;
+
+ intersects.push( {
+
+ distance: distance,
+ // What do we want? intersection point on the ray or on the segment??
+ // point: raycaster.ray.at( distance ),
+ point: interSegment.clone().applyMatrix4( this.matrixWorld ),
+ index: i,
+ face: null,
+ faceIndex: null,
+ object: this
+
+ } );
+
+ }
+
+ }
+
+ } else if ( geometry instanceof THREE.Geometry ) {
+
+ var vertices = geometry.vertices;
+ var nbVertices = vertices.length;
+
+ for ( var i = 0; i < nbVertices - 1; i += step ) {
+
+ var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment );
+
+ if ( distSq > precisionSq ) continue;
+
+ interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation
+
+ var distance = raycaster.ray.origin.distanceTo( interRay );
+
+ if ( distance < raycaster.near || distance > raycaster.far ) continue;
+
+ intersects.push( {
+
+ distance: distance,
+ // What do we want? intersection point on the ray or on the segment??
+ // point: raycaster.ray.at( distance ),
+ point: interSegment.clone().applyMatrix4( this.matrixWorld ),
+ index: i,
+ face: null,
+ faceIndex: null,
+ object: this
+
+ } );
+
+ }
+
+ }
+
+ };
+
+}() );
+
+THREE.Line.prototype.clone = function () {
+
+ return new this.constructor( this.geometry, this.material ).copy( this );
+
+};
+
+// DEPRECATED
+
+THREE.LineStrip = 0;
+THREE.LinePieces = 1;
+
+// File:src/objects/LineSegments.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.LineSegments = function ( geometry, material ) {
+
+ THREE.Line.call( this, geometry, material );
+
+ this.type = 'LineSegments';
+
+};
+
+THREE.LineSegments.prototype = Object.create( THREE.Line.prototype );
+THREE.LineSegments.prototype.constructor = THREE.LineSegments;
+
+// File:src/objects/Mesh.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author jonobr1 / http://jonobr1.com/
+ */
+
+THREE.Mesh = function ( geometry, material ) {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Mesh';
+
+ this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
+ this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } );
+
+ this.updateMorphTargets();
+
+};
+
+THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Mesh.prototype.constructor = THREE.Mesh;
+
+THREE.Mesh.prototype.updateMorphTargets = function () {
+
+ if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) {
+
+ this.morphTargetBase = - 1;
+ this.morphTargetInfluences = [];
+ this.morphTargetDictionary = {};
+
+ for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) {
+
+ this.morphTargetInfluences.push( 0 );
+ this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m;
+
+ }
+
+ }
+
+};
+
+THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) {
+
+ if ( this.morphTargetDictionary[ name ] !== undefined ) {
+
+ return this.morphTargetDictionary[ name ];
+
+ }
+
+ console.warn( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' );
+
+ return 0;
+
+};
+
+
+THREE.Mesh.prototype.raycast = ( function () {
+
+ var inverseMatrix = new THREE.Matrix4();
+ var ray = new THREE.Ray();
+ var sphere = new THREE.Sphere();
+
+ var vA = new THREE.Vector3();
+ var vB = new THREE.Vector3();
+ var vC = new THREE.Vector3();
+
+ var tempA = new THREE.Vector3();
+ var tempB = new THREE.Vector3();
+ var tempC = new THREE.Vector3();
+
+ var uvA = new THREE.Vector2();
+ var uvB = new THREE.Vector2();
+ var uvC = new THREE.Vector2();
+
+ var barycoord = new THREE.Vector3();
+
+ var intersectionPoint = new THREE.Vector3();
+ var intersectionPointWorld = new THREE.Vector3();
+
+ function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) {
+
+ THREE.Triangle.barycoordFromPoint( point, p1, p2, p3, barycoord );
+
+ uv1.multiplyScalar( barycoord.x );
+ uv2.multiplyScalar( barycoord.y );
+ uv3.multiplyScalar( barycoord.z );
+
+ uv1.add( uv2 ).add( uv3 );
+
+ return uv1.clone();
+
+ }
+
+ function checkIntersection( object, raycaster, ray, pA, pB, pC, point ){
+
+ var intersect;
+ var material = object.material;
+
+ if ( material.side === THREE.BackSide ) {
+
+ intersect = ray.intersectTriangle( pC, pB, pA, true, point );
+
+ } else {
+
+ intersect = ray.intersectTriangle( pA, pB, pC, material.side !== THREE.DoubleSide, point );
+
+ }
+
+ if ( intersect === null ) return null;
+
+ intersectionPointWorld.copy( point );
+ intersectionPointWorld.applyMatrix4( object.matrixWorld );
+
+ var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld );
+
+ if ( distance < raycaster.near || distance > raycaster.far ) return null;
+
+ return {
+ distance: distance,
+ point: intersectionPointWorld.clone(),
+ object: object
+ };
+
+ }
+
+ function checkBufferGeometryIntersection( object, raycaster, ray, positions, uvs, a, b, c ) {
+
+ vA.fromArray( positions, a * 3 );
+ vB.fromArray( positions, b * 3 );
+ vC.fromArray( positions, c * 3 );
+
+ var intersection = checkIntersection( object, raycaster, ray, vA, vB, vC, intersectionPoint );
+
+ if ( intersection ) {
+
+ if ( uvs ) {
+
+ uvA.fromArray( uvs, a * 2 );
+ uvB.fromArray( uvs, b * 2 );
+ uvC.fromArray( uvs, c * 2 );
+
+ intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC );
+
+ }
+
+ intersection.face = new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) );
+ intersection.faceIndex = a;
+
+ }
+
+ return intersection;
+
+ }
+
+ return function raycast( raycaster, intersects ) {
+
+ var geometry = this.geometry;
+ var material = this.material;
+
+ if ( material === undefined ) return;
+
+ // Checking boundingSphere distance to ray
+
+ if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
+
+ var matrixWorld = this.matrixWorld;
+
+ sphere.copy( geometry.boundingSphere );
+ sphere.applyMatrix4( matrixWorld );
+
+ if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) return;
+
+ // Check boundingBox before continuing
+
+ inverseMatrix.getInverse( matrixWorld );
+ ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
+
+ if ( geometry.boundingBox !== null ) {
+
+ if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) return;
+
+ }
+
+ var uvs, intersection;
+
+ if ( geometry instanceof THREE.BufferGeometry ) {
+
+ var a, b, c;
+ var index = geometry.index;
+ var attributes = geometry.attributes;
+ var positions = attributes.position.array;
+
+ if ( attributes.uv !== undefined ){
+
+ uvs = attributes.uv.array;
+
+ }
+
+ if ( index !== null ) {
+
+ var indices = index.array;
+
+ for ( var i = 0, l = indices.length; i < l; i += 3 ) {
+
+ a = indices[ i ];
+ b = indices[ i + 1 ];
+ c = indices[ i + 2 ];
+
+ intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c );
+
+ if ( intersection ) {
+
+ intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics
+ intersects.push( intersection );
+
+ }
+
+ }
+
+ } else {
+
+
+ for ( var i = 0, l = positions.length; i < l; i += 9 ) {
+
+ a = i / 3;
+ b = a + 1;
+ c = a + 2;
+
+ intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c );
+
+ if ( intersection ) {
+
+ intersection.index = a; // triangle number in positions buffer semantics
+ intersects.push( intersection );
+
+ }
+
+ }
+
+ }
+
+ } else if ( geometry instanceof THREE.Geometry ) {
+
+ var fvA, fvB, fvC;
+ var isFaceMaterial = material instanceof THREE.MeshFaceMaterial;
+ var materials = isFaceMaterial === true ? material.materials : null;
+
+ var vertices = geometry.vertices;
+ var faces = geometry.faces;
+ var faceVertexUvs = geometry.faceVertexUvs[ 0 ];
+ if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs;
+
+ for ( var f = 0, fl = faces.length; f < fl; f ++ ) {
+
+ var face = faces[ f ];
+ var faceMaterial = isFaceMaterial === true ? materials[ face.materialIndex ] : material;
+
+ if ( faceMaterial === undefined ) continue;
+
+ fvA = vertices[ face.a ];
+ fvB = vertices[ face.b ];
+ fvC = vertices[ face.c ];
+
+ if ( faceMaterial.morphTargets === true ) {
+
+ var morphTargets = geometry.morphTargets;
+ var morphInfluences = this.morphTargetInfluences;
+
+ vA.set( 0, 0, 0 );
+ vB.set( 0, 0, 0 );
+ vC.set( 0, 0, 0 );
+
+ for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) {
+
+ var influence = morphInfluences[ t ];
+
+ if ( influence === 0 ) continue;
+
+ var targets = morphTargets[ t ].vertices;
+
+ vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence );
+ vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence );
+ vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence );
+
+ }
+
+ vA.add( fvA );
+ vB.add( fvB );
+ vC.add( fvC );
+
+ fvA = vA;
+ fvB = vB;
+ fvC = vC;
+
+ }
+
+ intersection = checkIntersection( this, raycaster, ray, fvA, fvB, fvC, intersectionPoint );
+
+ if ( intersection ) {
+
+ if ( uvs ) {
+
+ var uvs_f = uvs[ f ];
+ uvA.copy( uvs_f[ 0 ] );
+ uvB.copy( uvs_f[ 1 ] );
+ uvC.copy( uvs_f[ 2 ] );
+
+ intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC );
+
+ }
+
+ intersection.face = face;
+ intersection.faceIndex = f;
+ intersects.push( intersection );
+
+ }
+
+ }
+
+ }
+
+ };
+
+}() );
+
+THREE.Mesh.prototype.clone = function () {
+
+ return new this.constructor( this.geometry, this.material ).copy( this );
+
+};
+
+// File:src/objects/Bone.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author ikerr / http://verold.com
+ */
+
+THREE.Bone = function ( skin ) {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Bone';
+
+ this.skin = skin;
+
+};
+
+THREE.Bone.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Bone.prototype.constructor = THREE.Bone;
+
+THREE.Bone.prototype.copy = function ( source ) {
+
+ THREE.Object3D.prototype.copy.call( this, source );
+
+ this.skin = source.skin;
+
+ return this;
+
+};
+
+// File:src/objects/Skeleton.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author michael guerrero / http://realitymeltdown.com
+ * @author ikerr / http://verold.com
+ */
+
+THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) {
+
+ this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true;
+
+ this.identityMatrix = new THREE.Matrix4();
+
+ // copy the bone array
+
+ bones = bones || [];
+
+ this.bones = bones.slice( 0 );
+
+ // create a bone texture or an array of floats
+
+ if ( this.useVertexTexture ) {
+
+ // layout (1 matrix = 4 pixels)
+ // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
+ // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8)
+ // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16)
+ // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32)
+ // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)
+
+
+ var size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix
+ size = THREE.Math.nextPowerOfTwo( Math.ceil( size ) );
+ size = Math.max( size, 4 );
+
+ this.boneTextureWidth = size;
+ this.boneTextureHeight = size;
+
+ this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel
+ this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType );
+
+ } else {
+
+ this.boneMatrices = new Float32Array( 16 * this.bones.length );
+
+ }
+
+ // use the supplied bone inverses or calculate the inverses
+
+ if ( boneInverses === undefined ) {
+
+ this.calculateInverses();
+
+ } else {
+
+ if ( this.bones.length === boneInverses.length ) {
+
+ this.boneInverses = boneInverses.slice( 0 );
+
+ } else {
+
+ console.warn( 'THREE.Skeleton bonInverses is the wrong length.' );
+
+ this.boneInverses = [];
+
+ for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+ this.boneInverses.push( new THREE.Matrix4() );
+
+ }
+
+ }
+
+ }
+
+};
+
+THREE.Skeleton.prototype.calculateInverses = function () {
+
+ this.boneInverses = [];
+
+ for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+ var inverse = new THREE.Matrix4();
+
+ if ( this.bones[ b ] ) {
+
+ inverse.getInverse( this.bones[ b ].matrixWorld );
+
+ }
+
+ this.boneInverses.push( inverse );
+
+ }
+
+};
+
+THREE.Skeleton.prototype.pose = function () {
+
+ var bone;
+
+ // recover the bind-time world matrices
+
+ for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+ bone = this.bones[ b ];
+
+ if ( bone ) {
+
+ bone.matrixWorld.getInverse( this.boneInverses[ b ] );
+
+ }
+
+ }
+
+ // compute the local matrices, positions, rotations and scales
+
+ for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+ bone = this.bones[ b ];
+
+ if ( bone ) {
+
+ if ( bone.parent ) {
+
+ bone.matrix.getInverse( bone.parent.matrixWorld );
+ bone.matrix.multiply( bone.matrixWorld );
+
+ } else {
+
+ bone.matrix.copy( bone.matrixWorld );
+
+ }
+
+ bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
+
+ }
+
+ }
+
+};
+
+THREE.Skeleton.prototype.update = ( function () {
+
+ var offsetMatrix = new THREE.Matrix4();
+
+ return function update() {
+
+ // flatten bone matrices to array
+
+ for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+ // compute the offset between the current and the original transform
+
+ var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix;
+
+ offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] );
+ offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );
+
+ }
+
+ if ( this.useVertexTexture ) {
+
+ this.boneTexture.needsUpdate = true;
+
+ }
+
+ };
+
+} )();
+
+THREE.Skeleton.prototype.clone = function () {
+
+ return new THREE.Skeleton( this.bones, this.boneInverses, this.useVertexTexture );
+
+};
+
+// File:src/objects/SkinnedMesh.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author ikerr / http://verold.com
+ */
+
+THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
+
+ THREE.Mesh.call( this, geometry, material );
+
+ this.type = 'SkinnedMesh';
+
+ this.bindMode = "attached";
+ this.bindMatrix = new THREE.Matrix4();
+ this.bindMatrixInverse = new THREE.Matrix4();
+
+ // init bones
+
+ // TODO: remove bone creation as there is no reason (other than
+ // convenience) for THREE.SkinnedMesh to do this.
+
+ var bones = [];
+
+ if ( this.geometry && this.geometry.bones !== undefined ) {
+
+ var bone, gbone;
+
+ for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) {
+
+ gbone = this.geometry.bones[ b ];
+
+ bone = new THREE.Bone( this );
+ bones.push( bone );
+
+ bone.name = gbone.name;
+ bone.position.fromArray( gbone.pos );
+ bone.quaternion.fromArray( gbone.rotq );
+ if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl );
+
+ }
+
+ for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) {
+
+ gbone = this.geometry.bones[ b ];
+
+ if ( gbone.parent !== - 1 && gbone.parent !== null) {
+
+ bones[ gbone.parent ].add( bones[ b ] );
+
+ } else {
+
+ this.add( bones[ b ] );
+
+ }
+
+ }
+
+ }
+
+ this.normalizeSkinWeights();
+
+ this.updateMatrixWorld( true );
+ this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ), this.matrixWorld );
+
+};
+
+
+THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
+THREE.SkinnedMesh.prototype.constructor = THREE.SkinnedMesh;
+
+THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) {
+
+ this.skeleton = skeleton;
+
+ if ( bindMatrix === undefined ) {
+
+ this.updateMatrixWorld( true );
+
+ this.skeleton.calculateInverses();
+
+ bindMatrix = this.matrixWorld;
+
+ }
+
+ this.bindMatrix.copy( bindMatrix );
+ this.bindMatrixInverse.getInverse( bindMatrix );
+
+};
+
+THREE.SkinnedMesh.prototype.pose = function () {
+
+ this.skeleton.pose();
+
+};
+
+THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () {
+
+ if ( this.geometry instanceof THREE.Geometry ) {
+
+ for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) {
+
+ var sw = this.geometry.skinWeights[ i ];
+
+ var scale = 1.0 / sw.lengthManhattan();
+
+ if ( scale !== Infinity ) {
+
+ sw.multiplyScalar( scale );
+
+ } else {
+
+ sw.set( 1 ); // this will be normalized by the shader anyway
+
+ }
+
+ }
+
+ } else {
+
+ // skinning weights assumed to be normalized for THREE.BufferGeometry
+
+ }
+
+};
+
+THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) {
+
+ THREE.Mesh.prototype.updateMatrixWorld.call( this, true );
+
+ if ( this.bindMode === "attached" ) {
+
+ this.bindMatrixInverse.getInverse( this.matrixWorld );
+
+ } else if ( this.bindMode === "detached" ) {
+
+ this.bindMatrixInverse.getInverse( this.bindMatrix );
+
+ } else {
+
+ console.warn( 'THREE.SkinnedMesh unrecognized bindMode: ' + this.bindMode );
+
+ }
+
+};
+
+THREE.SkinnedMesh.prototype.clone = function() {
+
+ return new this.constructor( this.geometry, this.material, this.useVertexTexture ).copy( this );
+
+};
+
+// File:src/objects/LOD.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.LOD = function () {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'LOD';
+
+ Object.defineProperties( this, {
+ levels: {
+ enumerable: true,
+ value: []
+ },
+ objects: {
+ get: function () {
+
+ console.warn( 'THREE.LOD: .objects has been renamed to .levels.' );
+ return this.levels;
+
+ }
+ }
+ } );
+
+};
+
+
+THREE.LOD.prototype = Object.create( THREE.Object3D.prototype );
+THREE.LOD.prototype.constructor = THREE.LOD;
+
+THREE.LOD.prototype.addLevel = function ( object, distance ) {
+
+ if ( distance === undefined ) distance = 0;
+
+ distance = Math.abs( distance );
+
+ var levels = this.levels;
+
+ for ( var l = 0; l < levels.length; l ++ ) {
+
+ if ( distance < levels[ l ].distance ) {
+
+ break;
+
+ }
+
+ }
+
+ levels.splice( l, 0, { distance: distance, object: object } );
+
+ this.add( object );
+
+};
+
+THREE.LOD.prototype.getObjectForDistance = function ( distance ) {
+
+ var levels = this.levels;
+
+ for ( var i = 1, l = levels.length; i < l; i ++ ) {
+
+ if ( distance < levels[ i ].distance ) {
+
+ break;
+
+ }
+
+ }
+
+ return levels[ i - 1 ].object;
+
+};
+
+THREE.LOD.prototype.raycast = ( function () {
+
+ var matrixPosition = new THREE.Vector3();
+
+ return function raycast( raycaster, intersects ) {
+
+ matrixPosition.setFromMatrixPosition( this.matrixWorld );
+
+ var distance = raycaster.ray.origin.distanceTo( matrixPosition );
+
+ this.getObjectForDistance( distance ).raycast( raycaster, intersects );
+
+ };
+
+}() );
+
+THREE.LOD.prototype.update = function () {
+
+ var v1 = new THREE.Vector3();
+ var v2 = new THREE.Vector3();
+
+ return function update( camera ) {
+
+ var levels = this.levels;
+
+ if ( levels.length > 1 ) {
+
+ v1.setFromMatrixPosition( camera.matrixWorld );
+ v2.setFromMatrixPosition( this.matrixWorld );
+
+ var distance = v1.distanceTo( v2 );
+
+ levels[ 0 ].object.visible = true;
+
+ for ( var i = 1, l = levels.length; i < l; i ++ ) {
+
+ if ( distance >= levels[ i ].distance ) {
+
+ levels[ i - 1 ].object.visible = false;
+ levels[ i ].object.visible = true;
+
+ } else {
+
+ break;
+
+ }
+
+ }
+
+ for ( ; i < l; i ++ ) {
+
+ levels[ i ].object.visible = false;
+
+ }
+
+ }
+
+ };
+
+}();
+
+THREE.LOD.prototype.copy = function ( source ) {
+
+ THREE.Object3D.prototype.copy.call( this, source, false );
+
+ var levels = source.levels;
+
+ for ( var i = 0, l = levels.length; i < l; i ++ ) {
+
+ var level = levels[ i ];
+
+ this.addLevel( level.object.clone(), level.distance );
+
+ }
+
+ return this;
+
+};
+
+THREE.LOD.prototype.toJSON = function ( meta ) {
+
+ var data = THREE.Object3D.prototype.toJSON.call( this, meta );
+
+ data.object.levels = [];
+
+ var levels = this.levels;
+
+ for ( var i = 0, l = levels.length; i < l; i ++ ) {
+
+ var level = levels[ i ];
+
+ data.object.levels.push( {
+ object: level.object.uuid,
+ distance: level.distance
+ } );
+
+ }
+
+ return data;
+
+};
+
+// File:src/objects/Sprite.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Sprite = ( function () {
+
+ var indices = new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] );
+ var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0.5, - 0.5, 0, 0.5, 0.5, 0, - 0.5, 0.5, 0 ] );
+ var uvs = new Float32Array( [ 0, 0, 1, 0, 1, 1, 0, 1 ] );
+
+ var geometry = new THREE.BufferGeometry();
+ geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
+ geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+ geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
+
+ return function Sprite( material ) {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Sprite';
+
+ this.geometry = geometry;
+ this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial();
+
+ };
+
+} )();
+
+THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Sprite.prototype.constructor = THREE.Sprite;
+
+THREE.Sprite.prototype.raycast = ( function () {
+
+ var matrixPosition = new THREE.Vector3();
+
+ return function raycast( raycaster, intersects ) {
+
+ matrixPosition.setFromMatrixPosition( this.matrixWorld );
+
+ var distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition );
+ var guessSizeSq = this.scale.x * this.scale.y;
+
+ if ( distanceSq > guessSizeSq ) {
+
+ return;
+
+ }
+
+ intersects.push( {
+
+ distance: Math.sqrt( distanceSq ),
+ point: this.position,
+ face: null,
+ object: this
+
+ } );
+
+ };
+
+}() );
+
+THREE.Sprite.prototype.clone = function () {
+
+ return new this.constructor( this.material ).copy( this );
+
+};
+
+// Backwards compatibility
+
+THREE.Particle = THREE.Sprite;
+
+// File:src/objects/LensFlare.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.LensFlare = function ( texture, size, distance, blending, color ) {
+
+ THREE.Object3D.call( this );
+
+ this.lensFlares = [];
+
+ this.positionScreen = new THREE.Vector3();
+ this.customUpdateCallback = undefined;
+
+ if ( texture !== undefined ) {
+
+ this.add( texture, size, distance, blending, color );
+
+ }
+
+};
+
+THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype );
+THREE.LensFlare.prototype.constructor = THREE.LensFlare;
+
+
+/*
+ * Add: adds another flare
+ */
+
+THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) {
+
+ if ( size === undefined ) size = - 1;
+ if ( distance === undefined ) distance = 0;
+ if ( opacity === undefined ) opacity = 1;
+ if ( color === undefined ) color = new THREE.Color( 0xffffff );
+ if ( blending === undefined ) blending = THREE.NormalBlending;
+
+ distance = Math.min( distance, Math.max( 0, distance ) );
+
+ this.lensFlares.push( {
+ texture: texture, // THREE.Texture
+ size: size, // size in pixels (-1 = use texture.width)
+ distance: distance, // distance (0-1) from light source (0=at light source)
+ x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is in front z = 1 is back
+ scale: 1, // scale
+ rotation: 0, // rotation
+ opacity: opacity, // opacity
+ color: color, // color
+ blending: blending // blending
+ } );
+
+};
+
+/*
+ * Update lens flares update positions on all flares based on the screen position
+ * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way.
+ */
+
+THREE.LensFlare.prototype.updateLensFlares = function () {
+
+ var f, fl = this.lensFlares.length;
+ var flare;
+ var vecX = - this.positionScreen.x * 2;
+ var vecY = - this.positionScreen.y * 2;
+
+ for ( f = 0; f < fl; f ++ ) {
+
+ flare = this.lensFlares[ f ];
+
+ flare.x = this.positionScreen.x + vecX * flare.distance;
+ flare.y = this.positionScreen.y + vecY * flare.distance;
+
+ flare.wantedRotation = flare.x * Math.PI * 0.25;
+ flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;
+
+ }
+
+};
+
+THREE.LensFlare.prototype.copy = function ( source ) {
+
+ THREE.Object3D.prototype.copy.call( this, source );
+
+ this.positionScreen.copy( source.positionScreen );
+ this.customUpdateCallback = source.customUpdateCallback;
+
+ for ( var i = 0, l = source.lensFlares.length; i < l; i ++ ) {
+
+ this.lensFlares.push( source.lensFlares[ i ] );
+
+ }
+
+ return this;
+
+};
+
+// File:src/scenes/Scene.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Scene = function () {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Scene';
+
+ this.fog = null;
+ this.overrideMaterial = null;
+
+ this.autoUpdate = true; // checked by the renderer
+
+};
+
+THREE.Scene.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Scene.prototype.constructor = THREE.Scene;
+
+THREE.Scene.prototype.copy = function ( source ) {
+
+ THREE.Object3D.prototype.copy.call( this, source );
+
+ if ( source.fog !== null ) this.fog = source.fog.clone();
+ if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone();
+
+ this.autoUpdate = source.autoUpdate;
+ this.matrixAutoUpdate = source.matrixAutoUpdate;
+
+ return this;
+
+};
+
+// File:src/scenes/Fog.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Fog = function ( color, near, far ) {
+
+ this.name = '';
+
+ this.color = new THREE.Color( color );
+
+ this.near = ( near !== undefined ) ? near : 1;
+ this.far = ( far !== undefined ) ? far : 1000;
+
+};
+
+THREE.Fog.prototype.clone = function () {
+
+ return new THREE.Fog( this.color.getHex(), this.near, this.far );
+
+};
+
+// File:src/scenes/FogExp2.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.FogExp2 = function ( color, density ) {
+
+ this.name = '';
+
+ this.color = new THREE.Color( color );
+ this.density = ( density !== undefined ) ? density : 0.00025;
+
+};
+
+THREE.FogExp2.prototype.clone = function () {
+
+ return new THREE.FogExp2( this.color.getHex(), this.density );
+
+};
+
+// File:src/renderers/shaders/ShaderChunk.js
+
+THREE.ShaderChunk = {};
+
+// File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl
+
+THREE.ShaderChunk[ 'alphamap_fragment'] = "#ifdef USE_ALPHAMAP\n\n diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'alphamap_pars_fragment'] = "#ifdef USE_ALPHAMAP\n\n uniform sampler2D alphaMap;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl
+
+THREE.ShaderChunk[ 'alphatest_fragment'] = "#ifdef ALPHATEST\n\n if ( diffuseColor.a < ALPHATEST ) discard;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/aomap_fragment.glsl
+
+THREE.ShaderChunk[ 'aomap_fragment'] = "#ifdef USE_AOMAP\n\n totalAmbientLight *= ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/aomap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'aomap_pars_fragment'] = "#ifdef USE_AOMAP\n\n uniform sampler2D aoMap;\n uniform float aoMapIntensity;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/begin_vertex.glsl
+
+THREE.ShaderChunk[ 'begin_vertex'] = "\nvec3 transformed = vec3( position );\n";
+
+// File:src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl
+
+THREE.ShaderChunk[ 'beginnormal_vertex'] = "\nvec3 objectNormal = vec3( normal );\n";
+
+// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'bumpmap_pars_fragment'] = "#ifdef USE_BUMPMAP\n\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n\n\n\n vec2 dHdxy_fwd() {\n\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\n\n float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n return vec2( dBx, dBy );\n\n }\n\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm;\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n\n float fDet = dot( vSigmaX, R1 );\n\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n\n }\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl
+
+THREE.ShaderChunk[ 'color_fragment'] = "#ifdef USE_COLOR\n\n diffuseColor.rgb *= vColor;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'color_pars_fragment'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'color_pars_vertex'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl
+
+THREE.ShaderChunk[ 'color_vertex'] = "#ifdef USE_COLOR\n\n vColor.xyz = color.xyz;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/common.glsl
+
+THREE.ShaderChunk[ 'common'] = "#define PI 3.14159\n#define PI2 6.28318\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\n\nvec3 transformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz );\n\n}\n\nvec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );\n\n}\n\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n float distance = dot( planeNormal, point - pointOnPlane );\n\n return - distance * planeNormal + point;\n\n}\n\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return sign( dot( point - pointOnPlane, planeNormal ) );\n\n}\n\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n\n}\n\nfloat calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {\n\n if ( decayExponent > 0.0 ) {\n\n return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\n }\n\n return 1.0;\n\n}\n\nvec3 F_Schlick( in vec3 specularColor, in float dotLH ) {\n\n\n float fresnel = exp2( ( -5.55437 * dotLH - 6.98316 ) * dotLH );\n\n return ( 1.0 - specularColor ) * fresnel + specularColor;\n\n}\n\nfloat G_BlinnPhong_Implicit( /* in float dotNL, in float dotNV */ ) {\n\n\n return 0.25;\n\n}\n\nfloat D_BlinnPhong( in float shininess, in float dotNH ) {\n\n\n return ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n\n}\n\nvec3 BRDF_BlinnPhong( in vec3 specularColor, in float shininess, in vec3 normal, in vec3 lightDir, in vec3 viewDir ) {\n\n vec3 halfDir = normalize( lightDir + viewDir );\n\n float dotNH = saturate( dot( normal, halfDir ) );\n float dotLH = saturate( dot( lightDir, halfDir ) );\n\n vec3 F = F_Schlick( specularColor, dotLH );\n\n float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );\n\n float D = D_BlinnPhong( shininess, dotNH );\n\n return F * G * D;\n\n}\n\nvec3 inputToLinear( in vec3 a ) {\n\n #ifdef GAMMA_INPUT\n\n return pow( a, vec3( float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n\nvec3 linearToOutput( in vec3 a ) {\n\n #ifdef GAMMA_OUTPUT\n\n return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n";
+
+// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl
+
+THREE.ShaderChunk[ 'defaultnormal_vertex'] = "#ifdef FLIP_SIDED\n\n objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;\n";
+
+// File:src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl
+
+THREE.ShaderChunk[ 'displacementmap_vertex'] = "#ifdef USE_DISPLACEMENTMAP\n\n transformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'displacementmap_pars_vertex'] = "#ifdef USE_DISPLACEMENTMAP\n\n uniform sampler2D displacementMap;\n uniform float displacementScale;\n uniform float displacementBias;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl
+
+THREE.ShaderChunk[ 'emissivemap_fragment'] = "#ifdef USE_EMISSIVEMAP\n\n vec4 emissiveColor = texture2D( emissiveMap, vUv );\n\n emissiveColor.rgb = inputToLinear( emissiveColor.rgb );\n\n totalEmissiveLight *= emissiveColor.rgb;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/emissivemap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'emissivemap_pars_fragment'] = "#ifdef USE_EMISSIVEMAP\n\n uniform sampler2D emissiveMap;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl
+
+THREE.ShaderChunk[ 'envmap_fragment'] = "#ifdef USE_ENVMAP\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n #else\n\n vec3 reflectVec = vReflect;\n\n #endif\n\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n\n #ifdef ENVMAP_TYPE_CUBE\n vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n vec4 envColor = texture2D( envMap, sampleUV );\n\n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n #endif\n\n envColor.xyz = inputToLinear( envColor.xyz );\n\n #ifdef ENVMAP_BLENDING_MULTIPLY\n\n outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_MIX )\n\n outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_ADD )\n\n outgoingLight += envColor.xyz * specularStrength * reflectivity;\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'envmap_pars_fragment'] = "#ifdef USE_ENVMAP\n\n uniform float reflectivity;\n #ifdef ENVMAP_TYPE_CUBE\n uniform samplerCube envMap;\n #else\n uniform sampler2D envMap;\n #endif\n uniform float flipEnvMap;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n uniform float refractionRatio;\n\n #else\n\n varying vec3 vReflect;\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'envmap_pars_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n varying vec3 vReflect;\n\n uniform float refractionRatio;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl
+
+THREE.ShaderChunk[ 'envmap_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vReflect = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl
+
+THREE.ShaderChunk[ 'fog_fragment'] = "#ifdef USE_FOG\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n #else\n\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #endif\n\n #ifdef FOG_EXP2\n\n float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n\n #else\n\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n #endif\n \n outgoingLight = mix( outgoingLight, fogColor, fogFactor );\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'fog_pars_fragment'] = "#ifdef USE_FOG\n\n uniform vec3 fogColor;\n\n #ifdef FOG_EXP2\n\n uniform float fogDensity;\n\n #else\n\n uniform float fogNear;\n uniform float fogFar;\n #endif\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/hemilight_fragment.glsl
+
+THREE.ShaderChunk[ 'hemilight_fragment'] = "#if MAX_HEMI_LIGHTS > 0\n\n for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vec3 lightColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n totalAmbientLight += lightColor;\n\n }\n\n#endif\n\n";
+
+// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl
+
+THREE.ShaderChunk[ 'lightmap_fragment'] = "#ifdef USE_LIGHTMAP\n\n totalAmbientLight += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'lightmap_pars_fragment'] = "#ifdef USE_LIGHTMAP\n\n uniform sampler2D lightMap;\n uniform float lightMapIntensity;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'lights_lambert_pars_vertex'] = "#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl
+
+THREE.ShaderChunk[ 'lights_lambert_vertex'] = "vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vec3( 0.0 );\n\n#endif\n\nvec3 normal = normalize( transformedNormal );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lVector = pointLightPosition[ i ] - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * saturate( - dotProduct );\n\n #endif\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n #ifdef DOUBLE_SIDED\n\n float hemiDiffuseWeightBack = - 0.5 * dotProduct + 0.5;\n\n vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n #endif\n\n }\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl
+
+THREE.ShaderChunk[ 'lights_phong_fragment'] = "vec3 viewDir = normalize( vViewPosition );\n\nvec3 totalDiffuseLight = vec3( 0.0 );\nvec3 totalSpecularLight = vec3( 0.0 );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lightPosition = pointLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * cosineTerm;\n\n }\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'lights_phong_pars_fragment'] = "uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n\n varying vec3 vNormal;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'lights_phong_pars_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl
+
+THREE.ShaderChunk[ 'lights_phong_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n vWorldPosition = worldPosition.xyz;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl
+
+THREE.ShaderChunk[ 'linear_to_gamma_fragment'] = "\n outgoingLight = linearToOutput( outgoingLight );\n";
+
+// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl
+
+THREE.ShaderChunk[ 'logdepthbuf_fragment'] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'logdepthbuf_pars_fragment'] = "#ifdef USE_LOGDEPTHBUF\n\n uniform float logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'logdepthbuf_pars_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n uniform float logDepthBufFC;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl
+
+THREE.ShaderChunk[ 'logdepthbuf_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n vFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n #endif\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl
+
+THREE.ShaderChunk[ 'map_fragment'] = "#ifdef USE_MAP\n\n vec4 texelColor = texture2D( map, vUv );\n\n texelColor.xyz = inputToLinear( texelColor.xyz );\n\n diffuseColor *= texelColor;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'map_pars_fragment'] = "#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl
+
+THREE.ShaderChunk[ 'map_particle_fragment'] = "#ifdef USE_MAP\n\n diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'map_particle_pars_fragment'] = "#ifdef USE_MAP\n\n uniform vec4 offsetRepeat;\n uniform sampler2D map;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl
+
+THREE.ShaderChunk[ 'morphnormal_vertex'] = "#ifdef USE_MORPHNORMALS\n\n objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'morphtarget_pars_vertex'] = "#ifdef USE_MORPHTARGETS\n\n #ifndef USE_MORPHNORMALS\n\n uniform float morphTargetInfluences[ 8 ];\n\n #else\n\n uniform float morphTargetInfluences[ 4 ];\n\n #endif\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl
+
+THREE.ShaderChunk[ 'morphtarget_vertex'] = "#ifdef USE_MORPHTARGETS\n\n transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n #ifndef USE_MORPHNORMALS\n\n transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/normal_phong_fragment.glsl
+
+THREE.ShaderChunk[ 'normal_phong_fragment'] = "#ifndef FLAT_SHADED\n\n vec3 normal = normalize( vNormal );\n\n #ifdef DOUBLE_SIDED\n\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n #endif\n\n#else\n\n vec3 fdx = dFdx( vViewPosition );\n vec3 fdy = dFdy( vViewPosition );\n vec3 normal = normalize( cross( fdx, fdy ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n normal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n";
+
+// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'normalmap_pars_fragment'] = "#ifdef USE_NORMALMAP\n\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n\n\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n vec3 q0 = dFdx( eye_pos.xyz );\n vec3 q1 = dFdy( eye_pos.xyz );\n vec2 st0 = dFdx( vUv.st );\n vec2 st1 = dFdy( vUv.st );\n\n vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n vec3 N = normalize( surf_norm );\n\n vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n mapN.xy = normalScale * mapN.xy;\n mat3 tsn = mat3( S, T, N );\n return normalize( tsn * mapN );\n\n }\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/project_vertex.glsl
+
+THREE.ShaderChunk[ 'project_vertex'] = "#ifdef USE_SKINNING\n\n vec4 mvPosition = modelViewMatrix * skinned;\n\n#else\n\n vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;\n";
+
+// File:src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl
+
+THREE.ShaderChunk[ 'shadowmap_fragment'] = "#ifdef USE_SHADOWMAP\n\n for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n float texelSizeY = 1.0 / shadowMapSize[ i ].y;\n\n float shadow = 0.0;\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n bool isPointLight = shadowDarkness[ i ] < 0.0;\n\n if ( isPointLight ) {\n\n float realShadowDarkness = abs( shadowDarkness[ i ] );\n\n vec3 lightToPosition = vShadowCoord[ i ].xyz;\n\n #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n vec3 bd3D = normalize( lightToPosition );\n float dp = length( lightToPosition );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n\n #if defined( SHADOWMAP_TYPE_PCF )\n const float Dr = 1.25;\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n const float Dr = 2.25;\n #endif\n\n float os = Dr * 2.0 * texelSizeY;\n\n const vec3 Gsd = vec3( - 1, 0, 1 );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n shadow *= realShadowDarkness * ( 1.0 / 21.0 );\n\n #else \n vec3 bd3D = normalize( lightToPosition );\n float dp = length( lightToPosition );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n shadow *= realShadowDarkness;\n\n #endif\n\n } else {\n\n#endif \n float texelSizeX = 1.0 / shadowMapSize[ i ].x;\n\n vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n\n bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n bool inFrustum = all( inFrustumVec );\n\n bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n bool frustumTest = all( frustumTestVec );\n\n if ( frustumTest ) {\n\n #if defined( SHADOWMAP_TYPE_PCF )\n\n\n /*\n for ( float y = -1.25; y <= 1.25; y += 1.25 )\n for ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n if ( fDepth < shadowCoord.z )\n shadow += 1.0;\n }\n shadow /= 9.0;\n */\n\n shadowCoord.z += shadowBias[ i ];\n\n const float ShadowDelta = 1.0 / 9.0;\n\n float xPixelOffset = texelSizeX;\n float yPixelOffset = texelSizeY;\n\n float dx0 = - 1.25 * xPixelOffset;\n float dy0 = - 1.25 * yPixelOffset;\n float dx1 = 1.25 * xPixelOffset;\n float dy1 = 1.25 * yPixelOffset;\n\n float fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n shadow *= shadowDarkness[ i ];\n\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n\n shadowCoord.z += shadowBias[ i ];\n\n float xPixelOffset = texelSizeX;\n float yPixelOffset = texelSizeY;\n\n float dx0 = - 1.0 * xPixelOffset;\n float dy0 = - 1.0 * yPixelOffset;\n float dx1 = 1.0 * xPixelOffset;\n float dy1 = 1.0 * yPixelOffset;\n\n mat3 shadowKernel;\n mat3 depthKernel;\n\n depthKernel[ 0 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n depthKernel[ 0 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n depthKernel[ 0 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n depthKernel[ 1 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n depthKernel[ 1 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n depthKernel[ 1 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n depthKernel[ 2 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n depthKernel[ 2 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n depthKernel[ 2 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n vec3 shadowZ = vec3( shadowCoord.z );\n shadowKernel[ 0 ] = vec3( lessThan( depthKernel[ 0 ], shadowZ ) );\n shadowKernel[ 0 ] *= vec3( 0.25 );\n\n shadowKernel[ 1 ] = vec3( lessThan( depthKernel[ 1 ], shadowZ ) );\n shadowKernel[ 1 ] *= vec3( 0.25 );\n\n shadowKernel[ 2 ] = vec3( lessThan( depthKernel[ 2 ], shadowZ ) );\n shadowKernel[ 2 ] *= vec3( 0.25 );\n\n vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[ i ].xy );\n\n shadowKernel[ 0 ] = mix( shadowKernel[ 1 ], shadowKernel[ 0 ], fractionalCoord.x );\n shadowKernel[ 1 ] = mix( shadowKernel[ 2 ], shadowKernel[ 1 ], fractionalCoord.x );\n\n vec4 shadowValues;\n shadowValues.x = mix( shadowKernel[ 0 ][ 1 ], shadowKernel[ 0 ][ 0 ], fractionalCoord.y );\n shadowValues.y = mix( shadowKernel[ 0 ][ 2 ], shadowKernel[ 0 ][ 1 ], fractionalCoord.y );\n shadowValues.z = mix( shadowKernel[ 1 ][ 1 ], shadowKernel[ 1 ][ 0 ], fractionalCoord.y );\n shadowValues.w = mix( shadowKernel[ 1 ][ 2 ], shadowKernel[ 1 ][ 1 ], fractionalCoord.y );\n\n shadow = dot( shadowValues, vec4( 1.0 ) ) * shadowDarkness[ i ];\n\n #else \n shadowCoord.z += shadowBias[ i ];\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n shadow = shadowDarkness[ i ];\n\n #endif\n\n }\n\n#ifdef SHADOWMAP_DEBUG\n\n if ( inFrustum ) {\n\n if ( i == 0 ) {\n\n outgoingLight *= vec3( 1.0, 0.5, 0.0 );\n\n } else if ( i == 1 ) {\n\n outgoingLight *= vec3( 0.0, 1.0, 0.8 );\n\n } else {\n\n outgoingLight *= vec3( 0.0, 0.5, 1.0 );\n\n }\n\n }\n\n#endif\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n }\n\n#endif\n\n shadowMask = shadowMask * vec3( 1.0 - shadow );\n\n }\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'shadowmap_pars_fragment'] = "#ifdef USE_SHADOWMAP\n\n uniform sampler2D shadowMap[ MAX_SHADOWS ];\n uniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform float shadowBias[ MAX_SHADOWS ];\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n float unpackDepth( const in vec4 rgba_depth ) {\n\n const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n float depth = dot( rgba_depth, bit_shift );\n return depth;\n\n }\n\n #if defined(POINT_LIGHT_SHADOWS)\n\n\n void adjustShadowValue1K( const float testDepth, const vec4 textureData, const float bias, inout float shadowValue ) {\n\n const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n if ( testDepth >= dot( textureData, bitSh ) * 1000.0 + bias )\n shadowValue += 1.0;\n\n }\n\n\n vec2 cubeToUV( vec3 v, float texelSizeY ) {\n\n\n vec3 absV = abs( v );\n\n\n float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n absV *= scaleToCube;\n\n\n v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\n\n\n vec2 planar = v.xy;\n\n float almostATexel = 1.5 * texelSizeY;\n float almostOne = 1.0 - almostATexel;\n\n if ( absV.z >= almostOne ) {\n\n if ( v.z > 0.0 )\n planar.x = 4.0 - v.x;\n\n } else if ( absV.x >= almostOne ) {\n\n float signX = sign( v.x );\n planar.x = v.z * signX + 2.0 * signX;\n\n } else if ( absV.y >= almostOne ) {\n\n float signY = sign( v.y );\n planar.x = v.x + 2.0 * signY + 2.0;\n planar.y = v.z * signY - 2.0;\n\n }\n\n\n return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\n }\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'shadowmap_pars_vertex'] = "#ifdef USE_SHADOWMAP\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl
+
+THREE.ShaderChunk[ 'shadowmap_vertex'] = "#ifdef USE_SHADOWMAP\n\n for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n }\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl
+
+THREE.ShaderChunk[ 'skinbase_vertex'] = "#ifdef USE_SKINNING\n\n mat4 boneMatX = getBoneMatrix( skinIndex.x );\n mat4 boneMatY = getBoneMatrix( skinIndex.y );\n mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n mat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'skinning_pars_vertex'] = "#ifdef USE_SKINNING\n\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n\n #ifdef BONE_TEXTURE\n\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n\n mat4 getBoneMatrix( const in float i ) {\n\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n\n y = dy * ( y + 0.5 );\n\n vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n mat4 bone = mat4( v1, v2, v3, v4 );\n\n return bone;\n\n }\n\n #else\n\n uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n mat4 getBoneMatrix( const in float i ) {\n\n mat4 bone = boneGlobalMatrices[ int(i) ];\n return bone;\n\n }\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl
+
+THREE.ShaderChunk[ 'skinning_vertex'] = "#ifdef USE_SKINNING\n\n vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\n vec4 skinned = vec4( 0.0 );\n skinned += boneMatX * skinVertex * skinWeight.x;\n skinned += boneMatY * skinVertex * skinWeight.y;\n skinned += boneMatZ * skinVertex * skinWeight.z;\n skinned += boneMatW * skinVertex * skinWeight.w;\n skinned = bindMatrixInverse * skinned;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl
+
+THREE.ShaderChunk[ 'skinnormal_vertex'] = "#ifdef USE_SKINNING\n\n mat4 skinMatrix = mat4( 0.0 );\n skinMatrix += skinWeight.x * boneMatX;\n skinMatrix += skinWeight.y * boneMatY;\n skinMatrix += skinWeight.z * boneMatZ;\n skinMatrix += skinWeight.w * boneMatW;\n skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\n objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl
+
+THREE.ShaderChunk[ 'specularmap_fragment'] = "float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n\n#else\n\n specularStrength = 1.0;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'specularmap_pars_fragment'] = "#ifdef USE_SPECULARMAP\n\n uniform sampler2D specularMap;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/uv2_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'uv2_pars_fragment'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n varying vec2 vUv2;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/uv2_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'uv2_pars_vertex'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n attribute vec2 uv2;\n varying vec2 vUv2;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/uv2_vertex.glsl
+
+THREE.ShaderChunk[ 'uv2_vertex'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n vUv2 = uv2;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl
+
+THREE.ShaderChunk[ 'uv_pars_fragment'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl
+
+THREE.ShaderChunk[ 'uv_pars_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n\n#endif\n";
+
+// File:src/renderers/shaders/ShaderChunk/uv_vertex.glsl
+
+THREE.ShaderChunk[ 'uv_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif";
+
+// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl
+
+THREE.ShaderChunk[ 'worldpos_vertex'] = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n #ifdef USE_SKINNING\n\n vec4 worldPosition = modelMatrix * skinned;\n\n #else\n\n vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n\n #endif\n\n#endif\n";
+
+// File:src/renderers/shaders/UniformsUtils.js
+
+/**
+ * Uniform Utilities
+ */
+
+THREE.UniformsUtils = {
+
+ merge: function ( uniforms ) {
+
+ var merged = {};
+
+ for ( var u = 0; u < uniforms.length; u ++ ) {
+
+ var tmp = this.clone( uniforms[ u ] );
+
+ for ( var p in tmp ) {
+
+ merged[ p ] = tmp[ p ];
+
+ }
+
+ }
+
+ return merged;
+
+ },
+
+ clone: function ( uniforms_src ) {
+
+ var uniforms_dst = {};
+
+ for ( var u in uniforms_src ) {
+
+ uniforms_dst[ u ] = {};
+
+ for ( var p in uniforms_src[ u ] ) {
+
+ var parameter_src = uniforms_src[ u ][ p ];
+
+ if ( parameter_src instanceof THREE.Color ||
+ parameter_src instanceof THREE.Vector2 ||
+ parameter_src instanceof THREE.Vector3 ||
+ parameter_src instanceof THREE.Vector4 ||
+ parameter_src instanceof THREE.Matrix3 ||
+ parameter_src instanceof THREE.Matrix4 ||
+ parameter_src instanceof THREE.Texture ) {
+
+ uniforms_dst[ u ][ p ] = parameter_src.clone();
+
+ } else if ( Array.isArray( parameter_src ) ) {
+
+ uniforms_dst[ u ][ p ] = parameter_src.slice();
+
+ } else {
+
+ uniforms_dst[ u ][ p ] = parameter_src;
+
+ }
+
+ }
+
+ }
+
+ return uniforms_dst;
+
+ }
+
+};
+
+// File:src/renderers/shaders/UniformsLib.js
+
+/**
+ * Uniforms library for shared webgl shaders
+ */
+
+THREE.UniformsLib = {
+
+ common: {
+
+ "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) },
+ "opacity" : { type: "f", value: 1.0 },
+
+ "map" : { type: "t", value: null },
+ "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) },
+
+ "specularMap" : { type: "t", value: null },
+ "alphaMap" : { type: "t", value: null },
+
+ "envMap" : { type: "t", value: null },
+ "flipEnvMap" : { type: "f", value: - 1 },
+ "reflectivity" : { type: "f", value: 1.0 },
+ "refractionRatio" : { type: "f", value: 0.98 }
+
+ },
+
+ aomap: {
+
+ "aoMap" : { type: "t", value: null },
+ "aoMapIntensity" : { type: "f", value: 1 },
+
+ },
+
+ lightmap: {
+
+ "lightMap" : { type: "t", value: null },
+ "lightMapIntensity" : { type: "f", value: 1 },
+
+ },
+
+ emissivemap: {
+
+ "emissiveMap" : { type: "t", value: null },
+
+ },
+
+ bumpmap: {
+
+ "bumpMap" : { type: "t", value: null },
+ "bumpScale" : { type: "f", value: 1 }
+
+ },
+
+ normalmap: {
+
+ "normalMap" : { type: "t", value: null },
+ "normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) }
+
+ },
+
+ displacementmap: {
+
+ "displacementMap" : { type: "t", value: null },
+ "displacementScale" : { type: "f", value: 1 },
+ "displacementBias" : { type: "f", value: 0 }
+
+ },
+
+ fog : {
+
+ "fogDensity" : { type: "f", value: 0.00025 },
+ "fogNear" : { type: "f", value: 1 },
+ "fogFar" : { type: "f", value: 2000 },
+ "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }
+
+ },
+
+ lights: {
+
+ "ambientLightColor" : { type: "fv", value: [] },
+
+ "directionalLightDirection" : { type: "fv", value: [] },
+ "directionalLightColor" : { type: "fv", value: [] },
+
+ "hemisphereLightDirection" : { type: "fv", value: [] },
+ "hemisphereLightSkyColor" : { type: "fv", value: [] },
+ "hemisphereLightGroundColor" : { type: "fv", value: [] },
+
+ "pointLightColor" : { type: "fv", value: [] },
+ "pointLightPosition" : { type: "fv", value: [] },
+ "pointLightDistance" : { type: "fv1", value: [] },
+ "pointLightDecay" : { type: "fv1", value: [] },
+
+ "spotLightColor" : { type: "fv", value: [] },
+ "spotLightPosition" : { type: "fv", value: [] },
+ "spotLightDirection" : { type: "fv", value: [] },
+ "spotLightDistance" : { type: "fv1", value: [] },
+ "spotLightAngleCos" : { type: "fv1", value: [] },
+ "spotLightExponent" : { type: "fv1", value: [] },
+ "spotLightDecay" : { type: "fv1", value: [] }
+
+ },
+
+ points: {
+
+ "psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) },
+ "opacity" : { type: "f", value: 1.0 },
+ "size" : { type: "f", value: 1.0 },
+ "scale" : { type: "f", value: 1.0 },
+ "map" : { type: "t", value: null },
+ "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) },
+
+ "fogDensity" : { type: "f", value: 0.00025 },
+ "fogNear" : { type: "f", value: 1 },
+ "fogFar" : { type: "f", value: 2000 },
+ "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }
+
+ },
+
+ shadowmap: {
+
+ "shadowMap": { type: "tv", value: [] },
+ "shadowMapSize": { type: "v2v", value: [] },
+
+ "shadowBias" : { type: "fv1", value: [] },
+ "shadowDarkness": { type: "fv1", value: [] },
+
+ "shadowMatrix" : { type: "m4v", value: [] }
+
+ }
+
+};
+
+// File:src/renderers/shaders/ShaderLib.js
+
+/**
+ * Webgl Shader Library for three.js
+ *
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author mikael emtinger / http://gomo.se/
+ */
+
+
+THREE.ShaderLib = {
+
+ 'basic': {
+
+ uniforms: THREE.UniformsUtils.merge( [
+
+ THREE.UniformsLib[ "common" ],
+ THREE.UniformsLib[ "aomap" ],
+ THREE.UniformsLib[ "fog" ],
+ THREE.UniformsLib[ "shadowmap" ]
+
+ ] ),
+
+ vertexShader: [
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "uv_pars_vertex" ],
+ THREE.ShaderChunk[ "uv2_pars_vertex" ],
+ THREE.ShaderChunk[ "envmap_pars_vertex" ],
+ THREE.ShaderChunk[ "color_pars_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+ THREE.ShaderChunk[ "skinning_pars_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "uv_vertex" ],
+ THREE.ShaderChunk[ "uv2_vertex" ],
+ THREE.ShaderChunk[ "color_vertex" ],
+ THREE.ShaderChunk[ "skinbase_vertex" ],
+
+ " #ifdef USE_ENVMAP",
+
+ THREE.ShaderChunk[ "beginnormal_vertex" ],
+ THREE.ShaderChunk[ "morphnormal_vertex" ],
+ THREE.ShaderChunk[ "skinnormal_vertex" ],
+ THREE.ShaderChunk[ "defaultnormal_vertex" ],
+
+ " #endif",
+
+ THREE.ShaderChunk[ "begin_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_vertex" ],
+ THREE.ShaderChunk[ "skinning_vertex" ],
+ THREE.ShaderChunk[ "project_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ THREE.ShaderChunk[ "worldpos_vertex" ],
+ THREE.ShaderChunk[ "envmap_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform vec3 diffuse;",
+ "uniform float opacity;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "color_pars_fragment" ],
+ THREE.ShaderChunk[ "uv_pars_fragment" ],
+ THREE.ShaderChunk[ "uv2_pars_fragment" ],
+ THREE.ShaderChunk[ "map_pars_fragment" ],
+ THREE.ShaderChunk[ "alphamap_pars_fragment" ],
+ THREE.ShaderChunk[ "aomap_pars_fragment" ],
+ THREE.ShaderChunk[ "envmap_pars_fragment" ],
+ THREE.ShaderChunk[ "fog_pars_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+ THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ " vec3 outgoingLight = vec3( 0.0 );",
+ " vec4 diffuseColor = vec4( diffuse, opacity );",
+ " vec3 totalAmbientLight = vec3( 1.0 );", // hardwired
+ " vec3 shadowMask = vec3( 1.0 );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+ THREE.ShaderChunk[ "map_fragment" ],
+ THREE.ShaderChunk[ "color_fragment" ],
+ THREE.ShaderChunk[ "alphamap_fragment" ],
+ THREE.ShaderChunk[ "alphatest_fragment" ],
+ THREE.ShaderChunk[ "specularmap_fragment" ],
+ THREE.ShaderChunk[ "aomap_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_fragment" ],
+
+ " outgoingLight = diffuseColor.rgb * totalAmbientLight * shadowMask;",
+
+ THREE.ShaderChunk[ "envmap_fragment" ],
+
+ THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
+
+ THREE.ShaderChunk[ "fog_fragment" ],
+
+ " gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ 'lambert': {
+
+ uniforms: THREE.UniformsUtils.merge( [
+
+ THREE.UniformsLib[ "common" ],
+ THREE.UniformsLib[ "fog" ],
+ THREE.UniformsLib[ "lights" ],
+ THREE.UniformsLib[ "shadowmap" ],
+
+ {
+ "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }
+ }
+
+ ] ),
+
+ vertexShader: [
+
+ "#define LAMBERT",
+
+ "varying vec3 vLightFront;",
+
+ "#ifdef DOUBLE_SIDED",
+
+ " varying vec3 vLightBack;",
+
+ "#endif",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "uv_pars_vertex" ],
+ THREE.ShaderChunk[ "uv2_pars_vertex" ],
+ THREE.ShaderChunk[ "envmap_pars_vertex" ],
+ THREE.ShaderChunk[ "lights_lambert_pars_vertex" ],
+ THREE.ShaderChunk[ "color_pars_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+ THREE.ShaderChunk[ "skinning_pars_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "uv_vertex" ],
+ THREE.ShaderChunk[ "uv2_vertex" ],
+ THREE.ShaderChunk[ "color_vertex" ],
+
+ THREE.ShaderChunk[ "beginnormal_vertex" ],
+ THREE.ShaderChunk[ "morphnormal_vertex" ],
+ THREE.ShaderChunk[ "skinbase_vertex" ],
+ THREE.ShaderChunk[ "skinnormal_vertex" ],
+ THREE.ShaderChunk[ "defaultnormal_vertex" ],
+
+ THREE.ShaderChunk[ "begin_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_vertex" ],
+ THREE.ShaderChunk[ "skinning_vertex" ],
+ THREE.ShaderChunk[ "project_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ THREE.ShaderChunk[ "worldpos_vertex" ],
+ THREE.ShaderChunk[ "envmap_vertex" ],
+ THREE.ShaderChunk[ "lights_lambert_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform vec3 diffuse;",
+ "uniform vec3 emissive;",
+ "uniform float opacity;",
+
+ "uniform vec3 ambientLightColor;",
+
+ "varying vec3 vLightFront;",
+
+ "#ifdef DOUBLE_SIDED",
+
+ " varying vec3 vLightBack;",
+
+ "#endif",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "color_pars_fragment" ],
+ THREE.ShaderChunk[ "uv_pars_fragment" ],
+ THREE.ShaderChunk[ "uv2_pars_fragment" ],
+ THREE.ShaderChunk[ "map_pars_fragment" ],
+ THREE.ShaderChunk[ "alphamap_pars_fragment" ],
+ THREE.ShaderChunk[ "envmap_pars_fragment" ],
+ THREE.ShaderChunk[ "fog_pars_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+ THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ " vec3 outgoingLight = vec3( 0.0 );", // outgoing light does not have an alpha, the surface does
+ " vec4 diffuseColor = vec4( diffuse, opacity );",
+ " vec3 totalAmbientLight = ambientLightColor;",
+ " vec3 shadowMask = vec3( 1.0 );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+ THREE.ShaderChunk[ "map_fragment" ],
+ THREE.ShaderChunk[ "color_fragment" ],
+ THREE.ShaderChunk[ "alphamap_fragment" ],
+ THREE.ShaderChunk[ "alphatest_fragment" ],
+ THREE.ShaderChunk[ "specularmap_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_fragment" ],
+
+ " #ifdef DOUBLE_SIDED",
+
+ " if ( gl_FrontFacing )",
+ " outgoingLight += diffuseColor.rgb * ( vLightFront * shadowMask + totalAmbientLight ) + emissive;",
+ " else",
+ " outgoingLight += diffuseColor.rgb * ( vLightBack * shadowMask + totalAmbientLight ) + emissive;",
+
+ " #else",
+
+ " outgoingLight += diffuseColor.rgb * ( vLightFront * shadowMask + totalAmbientLight ) + emissive;",
+
+ " #endif",
+
+ THREE.ShaderChunk[ "envmap_fragment" ],
+
+ THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
+
+ THREE.ShaderChunk[ "fog_fragment" ],
+
+ " gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ 'phong': {
+
+ uniforms: THREE.UniformsUtils.merge( [
+
+ THREE.UniformsLib[ "common" ],
+ THREE.UniformsLib[ "aomap" ],
+ THREE.UniformsLib[ "lightmap" ],
+ THREE.UniformsLib[ "emissivemap" ],
+ THREE.UniformsLib[ "bumpmap" ],
+ THREE.UniformsLib[ "normalmap" ],
+ THREE.UniformsLib[ "displacementmap" ],
+ THREE.UniformsLib[ "fog" ],
+ THREE.UniformsLib[ "lights" ],
+ THREE.UniformsLib[ "shadowmap" ],
+
+ {
+ "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) },
+ "specular" : { type: "c", value: new THREE.Color( 0x111111 ) },
+ "shininess": { type: "f", value: 30 }
+ }
+
+ ] ),
+
+ vertexShader: [
+
+ "#define PHONG",
+
+ "varying vec3 vViewPosition;",
+
+ "#ifndef FLAT_SHADED",
+
+ " varying vec3 vNormal;",
+
+ "#endif",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "uv_pars_vertex" ],
+ THREE.ShaderChunk[ "uv2_pars_vertex" ],
+ THREE.ShaderChunk[ "displacementmap_pars_vertex" ],
+ THREE.ShaderChunk[ "envmap_pars_vertex" ],
+ THREE.ShaderChunk[ "lights_phong_pars_vertex" ],
+ THREE.ShaderChunk[ "color_pars_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+ THREE.ShaderChunk[ "skinning_pars_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "uv_vertex" ],
+ THREE.ShaderChunk[ "uv2_vertex" ],
+ THREE.ShaderChunk[ "color_vertex" ],
+
+ THREE.ShaderChunk[ "beginnormal_vertex" ],
+ THREE.ShaderChunk[ "morphnormal_vertex" ],
+ THREE.ShaderChunk[ "skinbase_vertex" ],
+ THREE.ShaderChunk[ "skinnormal_vertex" ],
+ THREE.ShaderChunk[ "defaultnormal_vertex" ],
+
+ "#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED
+
+ " vNormal = normalize( transformedNormal );",
+
+ "#endif",
+
+ THREE.ShaderChunk[ "begin_vertex" ],
+ THREE.ShaderChunk[ "displacementmap_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_vertex" ],
+ THREE.ShaderChunk[ "skinning_vertex" ],
+ THREE.ShaderChunk[ "project_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ " vViewPosition = - mvPosition.xyz;",
+
+ THREE.ShaderChunk[ "worldpos_vertex" ],
+ THREE.ShaderChunk[ "envmap_vertex" ],
+ THREE.ShaderChunk[ "lights_phong_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "#define PHONG",
+
+ "uniform vec3 diffuse;",
+ "uniform vec3 emissive;",
+ "uniform vec3 specular;",
+ "uniform float shininess;",
+ "uniform float opacity;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "color_pars_fragment" ],
+ THREE.ShaderChunk[ "uv_pars_fragment" ],
+ THREE.ShaderChunk[ "uv2_pars_fragment" ],
+ THREE.ShaderChunk[ "map_pars_fragment" ],
+ THREE.ShaderChunk[ "alphamap_pars_fragment" ],
+ THREE.ShaderChunk[ "aomap_pars_fragment" ],
+ THREE.ShaderChunk[ "lightmap_pars_fragment" ],
+ THREE.ShaderChunk[ "emissivemap_pars_fragment" ],
+ THREE.ShaderChunk[ "envmap_pars_fragment" ],
+ THREE.ShaderChunk[ "fog_pars_fragment" ],
+ THREE.ShaderChunk[ "lights_phong_pars_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+ THREE.ShaderChunk[ "bumpmap_pars_fragment" ],
+ THREE.ShaderChunk[ "normalmap_pars_fragment" ],
+ THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ " vec3 outgoingLight = vec3( 0.0 );",
+ " vec4 diffuseColor = vec4( diffuse, opacity );",
+ " vec3 totalAmbientLight = ambientLightColor;",
+ " vec3 totalEmissiveLight = emissive;",
+ " vec3 shadowMask = vec3( 1.0 );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+ THREE.ShaderChunk[ "map_fragment" ],
+ THREE.ShaderChunk[ "color_fragment" ],
+ THREE.ShaderChunk[ "alphamap_fragment" ],
+ THREE.ShaderChunk[ "alphatest_fragment" ],
+ THREE.ShaderChunk[ "specularmap_fragment" ],
+ THREE.ShaderChunk[ "normal_phong_fragment" ],
+ THREE.ShaderChunk[ "lightmap_fragment" ],
+ THREE.ShaderChunk[ "hemilight_fragment" ],
+ THREE.ShaderChunk[ "aomap_fragment" ],
+ THREE.ShaderChunk[ "emissivemap_fragment" ],
+
+ THREE.ShaderChunk[ "lights_phong_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_fragment" ],
+
+ "totalDiffuseLight *= shadowMask;",
+ "totalSpecularLight *= shadowMask;",
+
+ "#ifdef METAL",
+
+ " outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) * specular + totalSpecularLight + totalEmissiveLight;",
+
+ "#else",
+
+ " outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) + totalSpecularLight + totalEmissiveLight;",
+
+ "#endif",
+
+ THREE.ShaderChunk[ "envmap_fragment" ],
+
+ THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
+
+ THREE.ShaderChunk[ "fog_fragment" ],
+
+ " gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ 'points': {
+
+ uniforms: THREE.UniformsUtils.merge( [
+
+ THREE.UniformsLib[ "points" ],
+ THREE.UniformsLib[ "shadowmap" ]
+
+ ] ),
+
+ vertexShader: [
+
+ "uniform float size;",
+ "uniform float scale;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "color_pars_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "color_vertex" ],
+
+ " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+
+ " #ifdef USE_SIZEATTENUATION",
+ " gl_PointSize = size * ( scale / length( mvPosition.xyz ) );",
+ " #else",
+ " gl_PointSize = size;",
+ " #endif",
+
+ " gl_Position = projectionMatrix * mvPosition;",
+
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+ THREE.ShaderChunk[ "worldpos_vertex" ],
+ THREE.ShaderChunk[ "shadowmap_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform vec3 psColor;",
+ "uniform float opacity;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "color_pars_fragment" ],
+ THREE.ShaderChunk[ "map_particle_pars_fragment" ],
+ THREE.ShaderChunk[ "fog_pars_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ " vec3 outgoingLight = vec3( 0.0 );",
+ " vec4 diffuseColor = vec4( psColor, opacity );",
+ " vec3 shadowMask = vec3( 1.0 );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+ THREE.ShaderChunk[ "map_particle_fragment" ],
+ THREE.ShaderChunk[ "color_fragment" ],
+ THREE.ShaderChunk[ "alphatest_fragment" ],
+ THREE.ShaderChunk[ "shadowmap_fragment" ],
+
+ " outgoingLight = diffuseColor.rgb * shadowMask;",
+
+ THREE.ShaderChunk[ "fog_fragment" ],
+
+ " gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ 'dashed': {
+
+ uniforms: THREE.UniformsUtils.merge( [
+
+ THREE.UniformsLib[ "common" ],
+ THREE.UniformsLib[ "fog" ],
+
+ {
+ "scale" : { type: "f", value: 1 },
+ "dashSize" : { type: "f", value: 1 },
+ "totalSize": { type: "f", value: 2 }
+ }
+
+ ] ),
+
+ vertexShader: [
+
+ "uniform float scale;",
+ "attribute float lineDistance;",
+
+ "varying float vLineDistance;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "color_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "color_vertex" ],
+
+ " vLineDistance = scale * lineDistance;",
+
+ " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+ " gl_Position = projectionMatrix * mvPosition;",
+
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform vec3 diffuse;",
+ "uniform float opacity;",
+
+ "uniform float dashSize;",
+ "uniform float totalSize;",
+
+ "varying float vLineDistance;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "color_pars_fragment" ],
+ THREE.ShaderChunk[ "fog_pars_fragment" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ " if ( mod( vLineDistance, totalSize ) > dashSize ) {",
+
+ " discard;",
+
+ " }",
+
+ " vec3 outgoingLight = vec3( 0.0 );",
+ " vec4 diffuseColor = vec4( diffuse, opacity );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+ THREE.ShaderChunk[ "color_fragment" ],
+
+ " outgoingLight = diffuseColor.rgb;", // simple shader
+
+ THREE.ShaderChunk[ "fog_fragment" ],
+
+ " gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ 'depth': {
+
+ uniforms: {
+
+ "mNear": { type: "f", value: 1.0 },
+ "mFar" : { type: "f", value: 2000.0 },
+ "opacity" : { type: "f", value: 1.0 }
+
+ },
+
+ vertexShader: [
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "begin_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_vertex" ],
+ THREE.ShaderChunk[ "project_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform float mNear;",
+ "uniform float mFar;",
+ "uniform float opacity;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+
+ " #ifdef USE_LOGDEPTHBUF_EXT",
+
+ " float depth = gl_FragDepthEXT / gl_FragCoord.w;",
+
+ " #else",
+
+ " float depth = gl_FragCoord.z / gl_FragCoord.w;",
+
+ " #endif",
+
+ " float color = 1.0 - smoothstep( mNear, mFar, depth );",
+ " gl_FragColor = vec4( vec3( color ), opacity );",
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ 'normal': {
+
+ uniforms: {
+
+ "opacity" : { type: "f", value: 1.0 }
+
+ },
+
+ vertexShader: [
+
+ "varying vec3 vNormal;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ " vNormal = normalize( normalMatrix * normal );",
+
+ THREE.ShaderChunk[ "begin_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_vertex" ],
+ THREE.ShaderChunk[ "project_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform float opacity;",
+ "varying vec3 vNormal;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ " gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ /* -------------------------------------------------------------------------
+ // Cube map shader
+ ------------------------------------------------------------------------- */
+
+ 'cube': {
+
+ uniforms: { "tCube": { type: "t", value: null },
+ "tFlip": { type: "f", value: - 1 } },
+
+ vertexShader: [
+
+ "varying vec3 vWorldPosition;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ " vWorldPosition = transformDirection( position, modelMatrix );",
+
+ " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform samplerCube tCube;",
+ "uniform float tFlip;",
+
+ "varying vec3 vWorldPosition;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ /* -------------------------------------------------------------------------
+ // Cube map shader
+ ------------------------------------------------------------------------- */
+
+ 'equirect': {
+
+ uniforms: { "tEquirect": { type: "t", value: null },
+ "tFlip": { type: "f", value: - 1 } },
+
+ vertexShader: [
+
+ "varying vec3 vWorldPosition;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ " vWorldPosition = transformDirection( position, modelMatrix );",
+
+ " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform sampler2D tEquirect;",
+ "uniform float tFlip;",
+
+ "varying vec3 vWorldPosition;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "void main() {",
+
+ // " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",
+ "vec3 direction = normalize( vWorldPosition );",
+ "vec2 sampleUV;",
+ "sampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );",
+ "sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;",
+ "gl_FragColor = texture2D( tEquirect, sampleUV );",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+ /* Depth encoding into RGBA texture
+ *
+ * based on SpiderGL shadow map example
+ * http://spidergl.org/example.php?id=6
+ *
+ * originally from
+ * http://www.gamedev.net/topic/442138-packing-a-float-into-a-a8r8g8b8-texture-shader/page__whichpage__1%25EF%25BF%25BD
+ *
+ * see also
+ * http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/
+ */
+
+ 'depthRGBA': {
+
+ uniforms: {},
+
+ vertexShader: [
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+ THREE.ShaderChunk[ "skinning_pars_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "skinbase_vertex" ],
+
+ THREE.ShaderChunk[ "begin_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_vertex" ],
+ THREE.ShaderChunk[ "skinning_vertex" ],
+ THREE.ShaderChunk[ "project_vertex" ],
+ THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+ "vec4 pack_depth( const in float depth ) {",
+
+ " const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );",
+ " const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );",
+ " vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", // " vec4 res = fract( depth * bit_shift );",
+ " res -= res.xxyz * bit_mask;",
+ " return res;",
+
+ "}",
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+
+ " #ifdef USE_LOGDEPTHBUF_EXT",
+
+ " gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );",
+
+ " #else",
+
+ " gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );",
+
+ " #endif",
+
+ //"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z / gl_FragCoord.w );",
+ //"float z = ( ( gl_FragCoord.z / gl_FragCoord.w ) - 3.0 ) / ( 4000.0 - 3.0 );",
+ //"gl_FragData[ 0 ] = pack_depth( z );",
+ //"gl_FragData[ 0 ] = vec4( z, z, z, 1.0 );",
+
+ "}"
+
+ ].join( "\n" )
+
+ },
+
+
+ 'distanceRGBA': {
+
+ uniforms: {
+
+ "lightPos": { type: "v3", value: new THREE.Vector3( 0, 0, 0 ) }
+
+ },
+
+ vertexShader: [
+
+ "varying vec4 vWorldPosition;",
+
+ THREE.ShaderChunk[ "common" ],
+ THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+ THREE.ShaderChunk[ "skinning_pars_vertex" ],
+
+ "void main() {",
+
+ THREE.ShaderChunk[ "skinbase_vertex" ],
+ THREE.ShaderChunk[ "begin_vertex" ],
+ THREE.ShaderChunk[ "morphtarget_vertex" ],
+ THREE.ShaderChunk[ "skinning_vertex" ],
+ THREE.ShaderChunk[ "project_vertex" ],
+ THREE.ShaderChunk[ "worldpos_vertex" ],
+
+ "vWorldPosition = worldPosition;",
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform vec3 lightPos;",
+ "varying vec4 vWorldPosition;",
+
+ THREE.ShaderChunk[ "common" ],
+
+ "vec4 pack1K ( float depth ) {",
+
+ " depth /= 1000.0;",
+ " const vec4 bitSh = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );",
+ " const vec4 bitMsk = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );",
+ " vec4 res = fract( depth * bitSh );",
+ " res -= res.xxyz * bitMsk;",
+ " return res; ",
+
+ "}",
+
+ "float unpack1K ( vec4 color ) {",
+
+ " const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );",
+ " return dot( color, bitSh ) * 1000.0;",
+
+ "}",
+
+ "void main () {",
+
+ " gl_FragColor = pack1K( length( vWorldPosition.xyz - lightPos.xyz ) );",
+
+ "}"
+
+ ].join( "\n" )
+
+ }
+
+};
+
+// File:src/renderers/WebGLRenderer.js
+
+/**
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author szimek / https://github.com/szimek/
+ */
+
+THREE.WebGLRenderer = function ( parameters ) {
+
+ console.log( 'THREE.WebGLRenderer', THREE.REVISION );
+
+ parameters = parameters || {};
+
+ var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ),
+ _context = parameters.context !== undefined ? parameters.context : null,
+
+ _width = _canvas.width,
+ _height = _canvas.height,
+
+ pixelRatio = 1,
+
+ _alpha = parameters.alpha !== undefined ? parameters.alpha : false,
+ _depth = parameters.depth !== undefined ? parameters.depth : true,
+ _stencil = parameters.stencil !== undefined ? parameters.stencil : true,
+ _antialias = parameters.antialias !== undefined ? parameters.antialias : false,
+ _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
+ _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
+
+ _clearColor = new THREE.Color( 0x000000 ),
+ _clearAlpha = 0;
+
+ var lights = [];
+
+ var opaqueObjects = [];
+ var opaqueObjectsLastIndex = - 1;
+ var transparentObjects = [];
+ var transparentObjectsLastIndex = - 1;
+
+ var morphInfluences = new Float32Array( 8 );
+
+
+ var sprites = [];
+ var lensFlares = [];
+
+ // public properties
+
+ this.domElement = _canvas;
+ this.context = null;
+
+ // clearing
+
+ this.autoClear = true;
+ this.autoClearColor = true;
+ this.autoClearDepth = true;
+ this.autoClearStencil = true;
+
+ // scene graph
+
+ this.sortObjects = true;
+
+ // physically based shading
+
+ this.gammaFactor = 2.0; // for backwards compatibility
+ this.gammaInput = false;
+ this.gammaOutput = false;
+
+ // morphs
+
+ this.maxMorphTargets = 8;
+ this.maxMorphNormals = 4;
+
+ // flags
+
+ this.autoScaleCubemaps = true;
+
+ // internal properties
+
+ var _this = this,
+
+ // internal state cache
+
+ _currentProgram = null,
+ _currentFramebuffer = null,
+ _currentMaterialId = - 1,
+ _currentGeometryProgram = '',
+ _currentCamera = null,
+
+ _usedTextureUnits = 0,
+
+ _viewportX = 0,
+ _viewportY = 0,
+ _viewportWidth = _canvas.width,
+ _viewportHeight = _canvas.height,
+ _currentWidth = 0,
+ _currentHeight = 0,
+
+ // frustum
+
+ _frustum = new THREE.Frustum(),
+
+ // camera matrices cache
+
+ _projScreenMatrix = new THREE.Matrix4(),
+
+ _vector3 = new THREE.Vector3(),
+
+ // light arrays cache
+
+ _direction = new THREE.Vector3(),
+
+ _lightsNeedUpdate = true,
+
+ _lights = {
+
+ ambient: [ 0, 0, 0 ],
+ directional: { length: 0, colors: [], positions: [] },
+ point: { length: 0, colors: [], positions: [], distances: [], decays: [] },
+ spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [], decays: [] },
+ hemi: { length: 0, skyColors: [], groundColors: [], positions: [] }
+
+ },
+
+ // info
+
+ _infoMemory = {
+
+ geometries: 0,
+ textures: 0
+
+ },
+
+ _infoRender = {
+
+ calls: 0,
+ vertices: 0,
+ faces: 0,
+ points: 0
+
+ };
+
+ this.info = {
+
+ render: _infoRender,
+ memory: _infoMemory,
+ programs: null
+
+ };
+
+
+ // initialize
+
+ var _gl;
+
+ try {
+
+ var attributes = {
+ alpha: _alpha,
+ depth: _depth,
+ stencil: _stencil,
+ antialias: _antialias,
+ premultipliedAlpha: _premultipliedAlpha,
+ preserveDrawingBuffer: _preserveDrawingBuffer
+ };
+
+ _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes );
+
+ if ( _gl === null ) {
+
+ if ( _canvas.getContext( 'webgl' ) !== null ) {
+
+ throw 'Error creating WebGL context with your selected attributes.';
+
+ } else {
+
+ throw 'Error creating WebGL context.';
+
+ }
+
+ }
+
+ _canvas.addEventListener( 'webglcontextlost', onContextLost, false );
+
+ } catch ( error ) {
+
+ console.error( 'THREE.WebGLRenderer: ' + error );
+
+ }
+
+ var extensions = new THREE.WebGLExtensions( _gl );
+
+ extensions.get( 'OES_texture_float' );
+ extensions.get( 'OES_texture_float_linear' );
+ extensions.get( 'OES_texture_half_float' );
+ extensions.get( 'OES_texture_half_float_linear' );
+ extensions.get( 'OES_standard_derivatives' );
+ extensions.get( 'ANGLE_instanced_arrays' );
+
+ if ( extensions.get( 'OES_element_index_uint' ) ) {
+
+ THREE.BufferGeometry.MaxIndex = 4294967296;
+
+ }
+
+ var capabilities = new THREE.WebGLCapabilities( _gl, extensions, parameters );
+
+ var state = new THREE.WebGLState( _gl, extensions, paramThreeToGL );
+ var properties = new THREE.WebGLProperties();
+ var objects = new THREE.WebGLObjects( _gl, properties, this.info );
+ var programCache = new THREE.WebGLPrograms( this, capabilities );
+
+ this.info.programs = programCache.programs;
+
+ var bufferRenderer = new THREE.WebGLBufferRenderer( _gl, extensions, _infoRender );
+ var indexedBufferRenderer = new THREE.WebGLIndexedBufferRenderer( _gl, extensions, _infoRender );
+
+ //
+
+ function glClearColor( r, g, b, a ) {
+
+ if ( _premultipliedAlpha === true ) {
+
+ r *= a; g *= a; b *= a;
+
+ }
+
+ _gl.clearColor( r, g, b, a );
+
+ }
+
+ function setDefaultGLState() {
+
+ state.init();
+
+ _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
+
+ glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+
+ }
+
+ function resetGLState() {
+
+ _currentProgram = null;
+ _currentCamera = null;
+
+ _currentGeometryProgram = '';
+ _currentMaterialId = - 1;
+
+ _lightsNeedUpdate = true;
+
+ state.reset();
+
+ }
+
+ setDefaultGLState();
+
+ this.context = _gl;
+ this.capabilities = capabilities;
+ this.extensions = extensions;
+ this.state = state;
+
+ // shadow map
+
+ var shadowMap = new THREE.WebGLShadowMap( this, lights, objects );
+
+ this.shadowMap = shadowMap;
+
+
+ // Plugins
+
+ var spritePlugin = new THREE.SpritePlugin( this, sprites );
+ var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares );
+
+ // API
+
+ this.getContext = function () {
+
+ return _gl;
+
+ };
+
+ this.getContextAttributes = function () {
+
+ return _gl.getContextAttributes();
+
+ };
+
+ this.forceContextLoss = function () {
+
+ extensions.get( 'WEBGL_lose_context' ).loseContext();
+
+ };
+
+ this.getMaxAnisotropy = ( function () {
+
+ var value;
+
+ return function getMaxAnisotropy() {
+
+ if ( value !== undefined ) return value;
+
+ var extension = extensions.get( 'EXT_texture_filter_anisotropic' );
+
+ if ( extension !== null ) {
+
+ value = _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT );
+
+ } else {
+
+ value = 0;
+
+ }
+
+ return value;
+
+ }
+
+ } )();
+
+ this.getPrecision = function () {
+
+ return capabilities.precision;
+
+ };
+
+ this.getPixelRatio = function () {
+
+ return pixelRatio;
+
+ };
+
+ this.setPixelRatio = function ( value ) {
+
+ if ( value !== undefined ) pixelRatio = value;
+
+ };
+
+ this.getSize = function () {
+
+ return {
+ width: _width,
+ height: _height
+ };
+
+ };
+
+ this.setSize = function ( width, height, updateStyle ) {
+
+ _width = width;
+ _height = height;
+
+ _canvas.width = width * pixelRatio;
+ _canvas.height = height * pixelRatio;
+
+ if ( updateStyle !== false ) {
+
+ _canvas.style.width = width + 'px';
+ _canvas.style.height = height + 'px';
+
+ }
+
+ this.setViewport( 0, 0, width, height );
+
+ };
+
+ this.setViewport = function ( x, y, width, height ) {
+
+ _viewportX = x * pixelRatio;
+ _viewportY = y * pixelRatio;
+
+ _viewportWidth = width * pixelRatio;
+ _viewportHeight = height * pixelRatio;
+
+ _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
+
+ };
+
+ this.getViewport = function ( dimensions ) {
+
+ dimensions.x = _viewportX / pixelRatio;
+ dimensions.y = _viewportY / pixelRatio;
+
+ dimensions.z = _viewportWidth / pixelRatio;
+ dimensions.w = _viewportHeight / pixelRatio;
+
+ };
+
+ this.setScissor = function ( x, y, width, height ) {
+
+ _gl.scissor(
+ x * pixelRatio,
+ y * pixelRatio,
+ width * pixelRatio,
+ height * pixelRatio
+ );
+
+ };
+
+ this.enableScissorTest = function ( boolean ) {
+
+ state.setScissorTest( boolean );
+
+ };
+
+ // Clearing
+
+ this.getClearColor = function () {
+
+ return _clearColor;
+
+ };
+
+ this.setClearColor = function ( color, alpha ) {
+
+ _clearColor.set( color );
+
+ _clearAlpha = alpha !== undefined ? alpha : 1;
+
+ glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+
+ };
+
+ this.getClearAlpha = function () {
+
+ return _clearAlpha;
+
+ };
+
+ this.setClearAlpha = function ( alpha ) {
+
+ _clearAlpha = alpha;
+
+ glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+
+ };
+
+ this.clear = function ( color, depth, stencil ) {
+
+ var bits = 0;
+
+ if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT;
+ if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT;
+ if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT;
+
+ _gl.clear( bits );
+
+ };
+
+ this.clearColor = function () {
+
+ _gl.clear( _gl.COLOR_BUFFER_BIT );
+
+ };
+
+ this.clearDepth = function () {
+
+ _gl.clear( _gl.DEPTH_BUFFER_BIT );
+
+ };
+
+ this.clearStencil = function () {
+
+ _gl.clear( _gl.STENCIL_BUFFER_BIT );
+
+ };
+
+ this.clearTarget = function ( renderTarget, color, depth, stencil ) {
+
+ this.setRenderTarget( renderTarget );
+ this.clear( color, depth, stencil );
+
+ };
+
+ // Reset
+
+ this.resetGLState = resetGLState;
+
+ this.dispose = function() {
+
+ _canvas.removeEventListener( 'webglcontextlost', onContextLost, false );
+
+ };
+
+ // Events
+
+ function onContextLost( event ) {
+
+ event.preventDefault();
+
+ resetGLState();
+ setDefaultGLState();
+
+ properties.clear();
+
+ };
+
+ function onTextureDispose( event ) {
+
+ var texture = event.target;
+
+ texture.removeEventListener( 'dispose', onTextureDispose );
+
+ deallocateTexture( texture );
+
+ _infoMemory.textures --;
+
+
+ }
+
+ function onRenderTargetDispose( event ) {
+
+ var renderTarget = event.target;
+
+ renderTarget.removeEventListener( 'dispose', onRenderTargetDispose );
+
+ deallocateRenderTarget( renderTarget );
+
+ _infoMemory.textures --;
+
+ }
+
+ function onMaterialDispose( event ) {
+
+ var material = event.target;
+
+ material.removeEventListener( 'dispose', onMaterialDispose );
+
+ deallocateMaterial( material );
+
+ }
+
+ // Buffer deallocation
+
+ function deallocateTexture( texture ) {
+
+ var textureProperties = properties.get( texture );
+
+ if ( texture.image && textureProperties.__image__webglTextureCube ) {
+
+ // cube texture
+
+ _gl.deleteTexture( textureProperties.__image__webglTextureCube );
+
+ } else {
+
+ // 2D texture
+
+ if ( textureProperties.__webglInit === undefined ) return;
+
+ _gl.deleteTexture( textureProperties.__webglTexture );
+
+ }
+
+ // remove all webgl properties
+ properties.delete( texture );
+
+ }
+
+ function deallocateRenderTarget( renderTarget ) {
+
+ var renderTargetProperties = properties.get( renderTarget );
+ var textureProperties = properties.get( renderTarget.texture );
+
+ if ( ! renderTarget || textureProperties.__webglTexture === undefined ) return;
+
+ _gl.deleteTexture( textureProperties.__webglTexture );
+
+ if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] );
+ _gl.deleteRenderbuffer( renderTargetProperties.__webglRenderbuffer[ i ] );
+
+ }
+
+ } else {
+
+ _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer );
+ _gl.deleteRenderbuffer( renderTargetProperties.__webglRenderbuffer );
+
+ }
+
+ properties.delete( renderTarget.texture );
+ properties.delete( renderTarget );
+
+ }
+
+ function deallocateMaterial( material ) {
+
+ releaseMaterialProgramReference( material );
+
+ properties.delete( material );
+
+ }
+
+
+ function releaseMaterialProgramReference( material ) {
+
+ var programInfo = properties.get( material ).program;
+
+ material.program = undefined;
+
+ if ( programInfo !== undefined ) {
+
+ programCache.releaseProgram( programInfo );
+
+ }
+
+ }
+
+ // Buffer rendering
+
+ this.renderBufferImmediate = function ( object, program, material ) {
+
+ state.initAttributes();
+
+ var buffers = properties.get( object );
+
+ if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer();
+ if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer();
+ if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer();
+ if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer();
+
+ var attributes = program.getAttributes();
+
+ if ( object.hasPositions ) {
+
+ _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position );
+ _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW );
+
+ state.enableAttribute( attributes.position );
+ _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
+
+ }
+
+ if ( object.hasNormals ) {
+
+ _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal );
+
+ if ( material.type !== 'MeshPhongMaterial' && material.shading === THREE.FlatShading ) {
+
+ for ( var i = 0, l = object.count * 3; i < l; i += 9 ) {
+
+ var array = object.normalArray;
+
+ var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3;
+ var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3;
+ var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3;
+
+ array[ i + 0 ] = nx;
+ array[ i + 1 ] = ny;
+ array[ i + 2 ] = nz;
+
+ array[ i + 3 ] = nx;
+ array[ i + 4 ] = ny;
+ array[ i + 5 ] = nz;
+
+ array[ i + 6 ] = nx;
+ array[ i + 7 ] = ny;
+ array[ i + 8 ] = nz;
+
+ }
+
+ }
+
+ _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW );
+
+ state.enableAttribute( attributes.normal );
+
+ _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 );
+
+ }
+
+ if ( object.hasUvs && material.map ) {
+
+ _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv );
+ _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW );
+
+ state.enableAttribute( attributes.uv );
+
+ _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 );
+
+ }
+
+ if ( object.hasColors && material.vertexColors !== THREE.NoColors ) {
+
+ _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color );
+ _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW );
+
+ state.enableAttribute( attributes.color );
+
+ _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 );
+
+ }
+
+ state.disableUnusedAttributes();
+
+ _gl.drawArrays( _gl.TRIANGLES, 0, object.count );
+
+ object.count = 0;
+
+ };
+
+ this.renderBufferDirect = function ( camera, lights, fog, geometry, material, object, group ) {
+
+ setMaterial( material );
+
+ var program = setProgram( camera, lights, fog, material, object );
+
+ var updateBuffers = false;
+ var geometryProgram = geometry.id + '_' + program.id + '_' + material.wireframe;
+
+ if ( geometryProgram !== _currentGeometryProgram ) {
+
+ _currentGeometryProgram = geometryProgram;
+ updateBuffers = true;
+
+ }
+
+ // morph targets
+
+ var morphTargetInfluences = object.morphTargetInfluences;
+
+ if ( morphTargetInfluences !== undefined ) {
+
+ var activeInfluences = [];
+
+ for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) {
+
+ var influence = morphTargetInfluences[ i ];
+ activeInfluences.push( [ influence, i ] );
+
+ }
+
+ activeInfluences.sort( numericalSort );
+
+ if ( activeInfluences.length > 8 ) {
+
+ activeInfluences.length = 8;
+
+ }
+
+ var morphAttributes = geometry.morphAttributes;
+
+ for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) {
+
+ var influence = activeInfluences[ i ];
+ morphInfluences[ i ] = influence[ 0 ];
+
+ if ( influence[ 0 ] !== 0 ) {
+
+ var index = influence[ 1 ];
+
+ if ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] );
+ if ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] );
+
+ } else {
+
+ if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i );
+ if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i );
+
+ }
+
+ }
+
+ var uniforms = program.getUniforms();
+
+ if ( uniforms.morphTargetInfluences !== null ) {
+
+ _gl.uniform1fv( uniforms.morphTargetInfluences, morphInfluences );
+
+ }
+
+ updateBuffers = true;
+
+ }
+
+ //
+
+ var index = geometry.index;
+ var position = geometry.attributes.position;
+
+ if ( material.wireframe === true ) {
+
+ index = objects.getWireframeAttribute( geometry );
+
+ }
+
+ var renderer;
+
+ if ( index !== null ) {
+
+ renderer = indexedBufferRenderer;
+ renderer.setIndex( index );
+
+ } else {
+
+ renderer = bufferRenderer;
+
+ }
+
+ if ( updateBuffers ) {
+
+ setupVertexAttributes( material, program, geometry );
+
+ if ( index !== null ) {
+
+ _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, objects.getAttributeBuffer( index ) );
+
+ }
+
+ }
+
+ //
+
+ var dataStart = 0;
+ var dataCount = Infinity;
+
+ if ( index !== null ) {
+
+ dataCount = index.count
+
+ } else if ( position !== undefined ) {
+
+ dataCount = position.count;
+
+ }
+
+ var rangeStart = geometry.drawRange.start;
+ var rangeCount = geometry.drawRange.count;
+
+ var groupStart = group !== null ? group.start : 0;
+ var groupCount = group !== null ? group.count : Infinity;
+
+ var drawStart = Math.max( dataStart, rangeStart, groupStart );
+ var drawEnd = Math.min( dataStart + dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1;
+
+ var drawCount = Math.max( 0, drawEnd - drawStart + 1 );
+
+ //
+
+ if ( object instanceof THREE.Mesh ) {
+
+ if ( material.wireframe === true ) {
+
+ state.setLineWidth( material.wireframeLinewidth * pixelRatio );
+ renderer.setMode( _gl.LINES );
+
+ } else {
+
+ renderer.setMode( _gl.TRIANGLES );
+
+ }
+
+ if ( geometry instanceof THREE.InstancedBufferGeometry && geometry.maxInstancedCount > 0 ) {
+
+ renderer.renderInstances( geometry );
+
+ } else {
+
+ renderer.render( drawStart, drawCount );
+
+ }
+
+ } else if ( object instanceof THREE.Line ) {
+
+ var lineWidth = material.linewidth;
+
+ if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material
+
+ state.setLineWidth( lineWidth * pixelRatio );
+
+ if ( object instanceof THREE.LineSegments ) {
+
+ renderer.setMode( _gl.LINES );
+
+ } else {
+
+ renderer.setMode( _gl.LINE_STRIP );
+
+ }
+
+ renderer.render( drawStart, drawCount );
+
+ } else if ( object instanceof THREE.Points ) {
+
+ renderer.setMode( _gl.POINTS );
+ renderer.render( drawStart, drawCount );
+
+ }
+
+ };
+
+ function setupVertexAttributes( material, program, geometry, startIndex ) {
+
+ var extension;
+
+ if ( geometry instanceof THREE.InstancedBufferGeometry ) {
+
+ extension = extensions.get( 'ANGLE_instanced_arrays' );
+
+ if ( extension === null ) {
+
+ console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
+ return;
+
+ }
+
+ }
+
+ if ( startIndex === undefined ) startIndex = 0;
+
+ state.initAttributes();
+
+ var geometryAttributes = geometry.attributes;
+
+ var programAttributes = program.getAttributes();
+
+ var materialDefaultAttributeValues = material.defaultAttributeValues;
+
+ for ( var name in programAttributes ) {
+
+ var programAttribute = programAttributes[ name ];
+
+ if ( programAttribute >= 0 ) {
+
+ var geometryAttribute = geometryAttributes[ name ];
+
+ if ( geometryAttribute !== undefined ) {
+
+ var size = geometryAttribute.itemSize;
+ var buffer = objects.getAttributeBuffer( geometryAttribute );
+
+ if ( geometryAttribute instanceof THREE.InterleavedBufferAttribute ) {
+
+ var data = geometryAttribute.data;
+ var stride = data.stride;
+ var offset = geometryAttribute.offset;
+
+ if ( data instanceof THREE.InstancedInterleavedBuffer ) {
+
+ state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute, extension );
+
+ if ( geometry.maxInstancedCount === undefined ) {
+
+ geometry.maxInstancedCount = data.meshPerAttribute * data.count;
+
+ }
+
+ } else {
+
+ state.enableAttribute( programAttribute );
+
+ }
+
+ _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );
+ _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, stride * data.array.BYTES_PER_ELEMENT, ( startIndex * stride + offset ) * data.array.BYTES_PER_ELEMENT );
+
+ } else {
+
+ if ( geometryAttribute instanceof THREE.InstancedBufferAttribute ) {
+
+ state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute, extension );
+
+ if ( geometry.maxInstancedCount === undefined ) {
+
+ geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;
+
+ }
+
+ } else {
+
+ state.enableAttribute( programAttribute );
+
+ }
+
+ _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );
+ _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32
+
+ }
+
+ } else if ( materialDefaultAttributeValues !== undefined ) {
+
+ var value = materialDefaultAttributeValues[ name ];
+
+ if ( value !== undefined ) {
+
+ switch ( value.length ) {
+
+ case 2:
+ _gl.vertexAttrib2fv( programAttribute, value );
+ break;
+
+ case 3:
+ _gl.vertexAttrib3fv( programAttribute, value );
+ break;
+
+ case 4:
+ _gl.vertexAttrib4fv( programAttribute, value );
+ break;
+
+ default:
+ _gl.vertexAttrib1fv( programAttribute, value );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ state.disableUnusedAttributes();
+
+ }
+
+ // Sorting
+
+ function numericalSort ( a, b ) {
+
+ return b[ 0 ] - a[ 0 ];
+
+ }
+
+ function painterSortStable ( a, b ) {
+
+ if ( a.object.renderOrder !== b.object.renderOrder ) {
+
+ return a.object.renderOrder - b.object.renderOrder;
+
+ } else if ( a.material.id !== b.material.id ) {
+
+ return a.material.id - b.material.id;
+
+ } else if ( a.z !== b.z ) {
+
+ return a.z - b.z;
+
+ } else {
+
+ return a.id - b.id;
+
+ }
+
+ }
+
+ function reversePainterSortStable ( a, b ) {
+
+ if ( a.object.renderOrder !== b.object.renderOrder ) {
+
+ return a.object.renderOrder - b.object.renderOrder;
+
+ } if ( a.z !== b.z ) {
+
+ return b.z - a.z;
+
+ } else {
+
+ return a.id - b.id;
+
+ }
+
+ }
+
+ // Rendering
+
+ this.render = function ( scene, camera, renderTarget, forceClear ) {
+
+ if ( camera instanceof THREE.Camera === false ) {
+
+ console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
+ return;
+
+ }
+
+ var fog = scene.fog;
+
+ // reset caching for this frame
+
+ _currentGeometryProgram = '';
+ _currentMaterialId = - 1;
+ _currentCamera = null;
+ _lightsNeedUpdate = true;
+
+ // update scene graph
+
+ if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+
+ // update camera matrices and frustum
+
+ if ( camera.parent === null ) camera.updateMatrixWorld();
+
+ camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+
+ _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+ _frustum.setFromMatrix( _projScreenMatrix );
+
+ lights.length = 0;
+
+ opaqueObjectsLastIndex = - 1;
+ transparentObjectsLastIndex = - 1;
+
+ sprites.length = 0;
+ lensFlares.length = 0;
+
+ projectObject( scene, camera );
+
+ opaqueObjects.length = opaqueObjectsLastIndex + 1;
+ transparentObjects.length = transparentObjectsLastIndex + 1;
+
+ if ( _this.sortObjects === true ) {
+
+ opaqueObjects.sort( painterSortStable );
+ transparentObjects.sort( reversePainterSortStable );
+
+ }
+
+ //
+
+ shadowMap.render( scene );
+
+ //
+
+ _infoRender.calls = 0;
+ _infoRender.vertices = 0;
+ _infoRender.faces = 0;
+ _infoRender.points = 0;
+
+ this.setRenderTarget( renderTarget );
+
+ if ( this.autoClear || forceClear ) {
+
+ this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil );
+
+ }
+
+ //
+
+ if ( scene.overrideMaterial ) {
+
+ var overrideMaterial = scene.overrideMaterial;
+
+ renderObjects( opaqueObjects, camera, lights, fog, overrideMaterial );
+ renderObjects( transparentObjects, camera, lights, fog, overrideMaterial );
+
+ } else {
+
+ // opaque pass (front-to-back order)
+
+ state.setBlending( THREE.NoBlending );
+ renderObjects( opaqueObjects, camera, lights, fog );
+
+ // transparent pass (back-to-front order)
+
+ renderObjects( transparentObjects, camera, lights, fog );
+
+ }
+
+ // custom render plugins (post pass)
+
+ spritePlugin.render( scene, camera );
+ lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight );
+
+ // Generate mipmap if we're using any kind of mipmap filtering
+
+ if ( renderTarget ) {
+
+ var texture = renderTarget.texture;
+ var isTargetPowerOfTwo = isPowerOfTwo( renderTarget );
+ if ( texture.generateMipmaps && isTargetPowerOfTwo && texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) {
+
+ updateRenderTargetMipmap( renderTarget );
+
+ }
+
+ }
+
+ // Ensure depth buffer writing is enabled so it can be cleared on next render
+
+ state.setDepthTest( true );
+ state.setDepthWrite( true );
+ state.setColorWrite( true );
+
+ // _gl.finish();
+
+ };
+
+ function pushRenderItem( object, geometry, material, z, group ) {
+
+ var array, index;
+
+ // allocate the next position in the appropriate array
+
+ if ( material.transparent ) {
+
+ array = transparentObjects;
+ index = ++ transparentObjectsLastIndex;
+
+ } else {
+
+ array = opaqueObjects;
+ index = ++ opaqueObjectsLastIndex;
+
+ }
+
+ // recycle existing render item or grow the array
+
+ var renderItem = array[ index ];
+
+ if ( renderItem !== undefined ) {
+
+ renderItem.id = object.id;
+ renderItem.object = object;
+ renderItem.geometry = geometry;
+ renderItem.material = material;
+ renderItem.z = _vector3.z;
+ renderItem.group = group;
+
+ } else {
+
+ renderItem = {
+ id: object.id,
+ object: object,
+ geometry: geometry,
+ material: material,
+ z: _vector3.z,
+ group: group
+ };
+
+ // assert( index === array.length );
+ array.push( renderItem );
+
+ }
+
+ }
+
+ function projectObject( object, camera ) {
+
+ if ( object.visible === false ) return;
+
+ if ( ( object.channels.mask & camera.channels.mask ) !== 0 ) {
+
+ if ( object instanceof THREE.Light ) {
+
+ lights.push( object );
+
+ } else if ( object instanceof THREE.Sprite ) {
+
+ sprites.push( object );
+
+ } else if ( object instanceof THREE.LensFlare ) {
+
+ lensFlares.push( object );
+
+ } else if ( object instanceof THREE.ImmediateRenderObject ) {
+
+ if ( _this.sortObjects === true ) {
+
+ _vector3.setFromMatrixPosition( object.matrixWorld );
+ _vector3.applyProjection( _projScreenMatrix );
+
+ }
+
+ pushRenderItem( object, null, object.material, _vector3.z, null );
+
+ } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) {
+
+ if ( object instanceof THREE.SkinnedMesh ) {
+
+ object.skeleton.update();
+
+ }
+
+ if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) {
+
+ var material = object.material;
+
+ if ( material.visible === true ) {
+
+ if ( _this.sortObjects === true ) {
+
+ _vector3.setFromMatrixPosition( object.matrixWorld );
+ _vector3.applyProjection( _projScreenMatrix );
+
+ }
+
+ var geometry = objects.update( object );
+
+ if ( material instanceof THREE.MeshFaceMaterial ) {
+
+ var groups = geometry.groups;
+ var materials = material.materials;
+
+ for ( var i = 0, l = groups.length; i < l; i ++ ) {
+
+ var group = groups[ i ];
+ var groupMaterial = materials[ group.materialIndex ];
+
+ if ( groupMaterial.visible === true ) {
+
+ pushRenderItem( object, geometry, groupMaterial, _vector3.z, group );
+
+ }
+
+ }
+
+ } else {
+
+ pushRenderItem( object, geometry, material, _vector3.z, null );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ var children = object.children;
+
+ for ( var i = 0, l = children.length; i < l; i ++ ) {
+
+ projectObject( children[ i ], camera );
+
+ }
+
+ }
+
+ function renderObjects( renderList, camera, lights, fog, overrideMaterial ) {
+
+ for ( var i = 0, l = renderList.length; i < l; i ++ ) {
+
+ var renderItem = renderList[ i ];
+
+ var object = renderItem.object;
+ var geometry = renderItem.geometry;
+ var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial;
+ var group = renderItem.group;
+
+ object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+ object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
+
+ if ( object instanceof THREE.ImmediateRenderObject ) {
+
+ setMaterial( material );
+
+ var program = setProgram( camera, lights, fog, material, object );
+
+ _currentGeometryProgram = '';
+
+ object.render( function ( object ) {
+
+ _this.renderBufferImmediate( object, program, material );
+
+ } );
+
+ } else {
+
+ _this.renderBufferDirect( camera, lights, fog, geometry, material, object, group );
+
+ }
+
+ }
+
+ }
+
+ function initMaterial( material, lights, fog, object ) {
+
+ var materialProperties = properties.get( material );
+
+ var parameters = programCache.getParameters( material, lights, fog, object );
+ var code = programCache.getProgramCode( material, parameters );
+
+ var program = materialProperties.program;
+ var programChange = true;
+
+ if ( program === undefined ) {
+
+ // new material
+ material.addEventListener( 'dispose', onMaterialDispose );
+
+ } else if ( program.code !== code ) {
+
+ // changed glsl or parameters
+ releaseMaterialProgramReference( material );
+
+ } else if ( parameters.shaderID !== undefined ) {
+
+ // same glsl and uniform list
+ return;
+
+ } else {
+
+ // only rebuild uniform list
+ programChange = false;
+
+ }
+
+ if ( programChange ) {
+
+ if ( parameters.shaderID ) {
+
+ var shader = THREE.ShaderLib[ parameters.shaderID ];
+
+ materialProperties.__webglShader = {
+ name: material.type,
+ uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
+ vertexShader: shader.vertexShader,
+ fragmentShader: shader.fragmentShader
+ };
+
+ } else {
+
+ materialProperties.__webglShader = {
+ name: material.type,
+ uniforms: material.uniforms,
+ vertexShader: material.vertexShader,
+ fragmentShader: material.fragmentShader
+ };
+
+ }
+
+ material.__webglShader = materialProperties.__webglShader;
+
+ program = programCache.acquireProgram( material, parameters, code );
+
+ materialProperties.program = program;
+ material.program = program;
+
+ }
+
+ var attributes = program.getAttributes();
+
+ if ( material.morphTargets ) {
+
+ material.numSupportedMorphTargets = 0;
+
+ for ( var i = 0; i < _this.maxMorphTargets; i ++ ) {
+
+ if ( attributes[ 'morphTarget' + i ] >= 0 ) {
+
+ material.numSupportedMorphTargets ++;
+
+ }
+
+ }
+
+ }
+
+ if ( material.morphNormals ) {
+
+ material.numSupportedMorphNormals = 0;
+
+ for ( i = 0; i < _this.maxMorphNormals; i ++ ) {
+
+ if ( attributes[ 'morphNormal' + i ] >= 0 ) {
+
+ material.numSupportedMorphNormals ++;
+
+ }
+
+ }
+
+ }
+
+ materialProperties.uniformsList = [];
+
+ var uniformLocations = materialProperties.program.getUniforms();
+
+ for ( var u in materialProperties.__webglShader.uniforms ) {
+
+ var location = uniformLocations[ u ];
+
+ if ( location ) {
+
+ materialProperties.uniformsList.push( [ materialProperties.__webglShader.uniforms[ u ], location ] );
+
+ }
+
+ }
+
+ }
+
+ function setMaterial( material ) {
+
+ setMaterialFaces( material );
+
+ if ( material.transparent === true ) {
+
+ state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha );
+
+ } else {
+
+ state.setBlending( THREE.NoBlending );
+
+ }
+
+ state.setDepthFunc( material.depthFunc );
+ state.setDepthTest( material.depthTest );
+ state.setDepthWrite( material.depthWrite );
+ state.setColorWrite( material.colorWrite );
+ state.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
+
+ }
+
+ function setMaterialFaces( material ) {
+
+ material.side !== THREE.DoubleSide ? state.enable( _gl.CULL_FACE ) : state.disable( _gl.CULL_FACE );
+ state.setFlipSided( material.side === THREE.BackSide );
+
+ }
+
+ function setProgram( camera, lights, fog, material, object ) {
+
+ _usedTextureUnits = 0;
+
+ var materialProperties = properties.get( material );
+
+ if ( material.needsUpdate || ! materialProperties.program ) {
+
+ initMaterial( material, lights, fog, object );
+ material.needsUpdate = false;
+
+ }
+
+ var refreshProgram = false;
+ var refreshMaterial = false;
+ var refreshLights = false;
+
+ var program = materialProperties.program,
+ p_uniforms = program.getUniforms(),
+ m_uniforms = materialProperties.__webglShader.uniforms;
+
+ if ( program.id !== _currentProgram ) {
+
+ _gl.useProgram( program.program );
+ _currentProgram = program.id;
+
+ refreshProgram = true;
+ refreshMaterial = true;
+ refreshLights = true;
+
+ }
+
+ if ( material.id !== _currentMaterialId ) {
+
+ if ( _currentMaterialId === - 1 ) refreshLights = true;
+ _currentMaterialId = material.id;
+
+ refreshMaterial = true;
+
+ }
+
+ if ( refreshProgram || camera !== _currentCamera ) {
+
+ _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
+
+ if ( capabilities.logarithmicDepthBuffer ) {
+
+ _gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
+
+ }
+
+
+ if ( camera !== _currentCamera ) _currentCamera = camera;
+
+ // load material specific uniforms
+ // (shader material also gets them for the sake of genericity)
+
+ if ( material instanceof THREE.ShaderMaterial ||
+ material instanceof THREE.MeshPhongMaterial ||
+ material.envMap ) {
+
+ if ( p_uniforms.cameraPosition !== undefined ) {
+
+ _vector3.setFromMatrixPosition( camera.matrixWorld );
+ _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z );
+
+ }
+
+ }
+
+ if ( material instanceof THREE.MeshPhongMaterial ||
+ material instanceof THREE.MeshLambertMaterial ||
+ material instanceof THREE.MeshBasicMaterial ||
+ material instanceof THREE.ShaderMaterial ||
+ material.skinning ) {
+
+ if ( p_uniforms.viewMatrix !== undefined ) {
+
+ _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements );
+
+ }
+
+ }
+
+ }
+
+ // skinning uniforms must be set even if material didn't change
+ // auto-setting of texture unit for bone texture must go before other textures
+ // not sure why, but otherwise weird things happen
+
+ if ( material.skinning ) {
+
+ if ( object.bindMatrix && p_uniforms.bindMatrix !== undefined ) {
+
+ _gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements );
+
+ }
+
+ if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== undefined ) {
+
+ _gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements );
+
+ }
+
+ if ( capabilities.floatVertexTextures && object.skeleton && object.skeleton.useVertexTexture ) {
+
+ if ( p_uniforms.boneTexture !== undefined ) {
+
+ var textureUnit = getTextureUnit();
+
+ _gl.uniform1i( p_uniforms.boneTexture, textureUnit );
+ _this.setTexture( object.skeleton.boneTexture, textureUnit );
+
+ }
+
+ if ( p_uniforms.boneTextureWidth !== undefined ) {
+
+ _gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth );
+
+ }
+
+ if ( p_uniforms.boneTextureHeight !== undefined ) {
+
+ _gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight );
+
+ }
+
+ } else if ( object.skeleton && object.skeleton.boneMatrices ) {
+
+ if ( p_uniforms.boneGlobalMatrices !== undefined ) {
+
+ _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices );
+
+ }
+
+ }
+
+ }
+
+ if ( refreshMaterial ) {
+
+ // refresh uniforms common to several materials
+
+ if ( fog && material.fog ) {
+
+ refreshUniformsFog( m_uniforms, fog );
+
+ }
+
+ if ( material instanceof THREE.MeshPhongMaterial ||
+ material instanceof THREE.MeshLambertMaterial ||
+ material.lights ) {
+
+ if ( _lightsNeedUpdate ) {
+
+ refreshLights = true;
+ setupLights( lights, camera );
+ _lightsNeedUpdate = false;
+
+ }
+
+ if ( refreshLights ) {
+
+ refreshUniformsLights( m_uniforms, _lights );
+ markUniformsLightsNeedsUpdate( m_uniforms, true );
+
+ } else {
+
+ markUniformsLightsNeedsUpdate( m_uniforms, false );
+
+ }
+
+ }
+
+ if ( material instanceof THREE.MeshBasicMaterial ||
+ material instanceof THREE.MeshLambertMaterial ||
+ material instanceof THREE.MeshPhongMaterial ) {
+
+ refreshUniformsCommon( m_uniforms, material );
+
+ }
+
+ // refresh single material specific uniforms
+
+ if ( material instanceof THREE.LineBasicMaterial ) {
+
+ refreshUniformsLine( m_uniforms, material );
+
+ } else if ( material instanceof THREE.LineDashedMaterial ) {
+
+ refreshUniformsLine( m_uniforms, material );
+ refreshUniformsDash( m_uniforms, material );
+
+ } else if ( material instanceof THREE.PointsMaterial ) {
+
+ refreshUniformsParticle( m_uniforms, material );
+
+ } else if ( material instanceof THREE.MeshPhongMaterial ) {
+
+ refreshUniformsPhong( m_uniforms, material );
+
+ } else if ( material instanceof THREE.MeshDepthMaterial ) {
+
+ m_uniforms.mNear.value = camera.near;
+ m_uniforms.mFar.value = camera.far;
+ m_uniforms.opacity.value = material.opacity;
+
+ } else if ( material instanceof THREE.MeshNormalMaterial ) {
+
+ m_uniforms.opacity.value = material.opacity;
+
+ }
+
+ if ( object.receiveShadow && ! material._shadowPass ) {
+
+ refreshUniformsShadow( m_uniforms, lights, camera );
+
+ }
+
+ // load common uniforms
+
+ loadUniformsGeneric( materialProperties.uniformsList );
+
+ }
+
+ loadUniformsMatrices( p_uniforms, object );
+
+ if ( p_uniforms.modelMatrix !== undefined ) {
+
+ _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements );
+
+ }
+
+ return program;
+
+ }
+
+ // Uniforms (refresh uniforms objects)
+
+ function refreshUniformsCommon ( uniforms, material ) {
+
+ uniforms.opacity.value = material.opacity;
+
+ uniforms.diffuse.value = material.color;
+
+ if ( material.emissive ) {
+
+ uniforms.emissive.value = material.emissive;
+
+ }
+
+ uniforms.map.value = material.map;
+ uniforms.specularMap.value = material.specularMap;
+ uniforms.alphaMap.value = material.alphaMap;
+
+ if ( material.aoMap ) {
+
+ uniforms.aoMap.value = material.aoMap;
+ uniforms.aoMapIntensity.value = material.aoMapIntensity;
+
+ }
+
+ // uv repeat and offset setting priorities
+ // 1. color map
+ // 2. specular map
+ // 3. normal map
+ // 4. bump map
+ // 5. alpha map
+ // 6. emissive map
+
+ var uvScaleMap;
+
+ if ( material.map ) {
+
+ uvScaleMap = material.map;
+
+ } else if ( material.specularMap ) {
+
+ uvScaleMap = material.specularMap;
+
+ } else if ( material.displacementMap ) {
+
+ uvScaleMap = material.displacementMap;
+
+ } else if ( material.normalMap ) {
+
+ uvScaleMap = material.normalMap;
+
+ } else if ( material.bumpMap ) {
+
+ uvScaleMap = material.bumpMap;
+
+ } else if ( material.alphaMap ) {
+
+ uvScaleMap = material.alphaMap;
+
+ } else if ( material.emissiveMap ) {
+
+ uvScaleMap = material.emissiveMap;
+
+ }
+
+ if ( uvScaleMap !== undefined ) {
+
+ if ( uvScaleMap instanceof THREE.WebGLRenderTarget ) uvScaleMap = uvScaleMap.texture;
+ var offset = uvScaleMap.offset;
+ var repeat = uvScaleMap.repeat;
+
+ uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );
+
+ }
+
+ uniforms.envMap.value = material.envMap;
+ uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : - 1;
+
+ uniforms.reflectivity.value = material.reflectivity;
+ uniforms.refractionRatio.value = material.refractionRatio;
+
+ }
+
+ function refreshUniformsLine ( uniforms, material ) {
+
+ uniforms.diffuse.value = material.color;
+ uniforms.opacity.value = material.opacity;
+
+ }
+
+ function refreshUniformsDash ( uniforms, material ) {
+
+ uniforms.dashSize.value = material.dashSize;
+ uniforms.totalSize.value = material.dashSize + material.gapSize;
+ uniforms.scale.value = material.scale;
+
+ }
+
+ function refreshUniformsParticle ( uniforms, material ) {
+
+ uniforms.psColor.value = material.color;
+ uniforms.opacity.value = material.opacity;
+ uniforms.size.value = material.size;
+ uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this.
+
+ uniforms.map.value = material.map;
+
+ if ( material.map !== null ) {
+
+ var offset = material.map.offset;
+ var repeat = material.map.repeat;
+
+ uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );
+
+ }
+
+ }
+
+ function refreshUniformsFog ( uniforms, fog ) {
+
+ uniforms.fogColor.value = fog.color;
+
+ if ( fog instanceof THREE.Fog ) {
+
+ uniforms.fogNear.value = fog.near;
+ uniforms.fogFar.value = fog.far;
+
+ } else if ( fog instanceof THREE.FogExp2 ) {
+
+ uniforms.fogDensity.value = fog.density;
+
+ }
+
+ }
+
+ function refreshUniformsPhong ( uniforms, material ) {
+
+ uniforms.specular.value = material.specular;
+ uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
+
+ if ( material.lightMap ) {
+
+ uniforms.lightMap.value = material.lightMap;
+ uniforms.lightMapIntensity.value = material.lightMapIntensity;
+
+ }
+
+ if ( material.emissiveMap ) {
+
+ uniforms.emissiveMap.value = material.emissiveMap;
+
+ }
+
+ if ( material.bumpMap ) {
+
+ uniforms.bumpMap.value = material.bumpMap;
+ uniforms.bumpScale.value = material.bumpScale;
+
+ }
+
+ if ( material.normalMap ) {
+
+ uniforms.normalMap.value = material.normalMap;
+ uniforms.normalScale.value.copy( material.normalScale );
+
+ }
+
+ if ( material.displacementMap ) {
+
+ uniforms.displacementMap.value = material.displacementMap;
+ uniforms.displacementScale.value = material.displacementScale;
+ uniforms.displacementBias.value = material.displacementBias;
+
+ }
+
+ }
+
+ function refreshUniformsLights ( uniforms, lights ) {
+
+ uniforms.ambientLightColor.value = lights.ambient;
+
+ uniforms.directionalLightColor.value = lights.directional.colors;
+ uniforms.directionalLightDirection.value = lights.directional.positions;
+
+ uniforms.pointLightColor.value = lights.point.colors;
+ uniforms.pointLightPosition.value = lights.point.positions;
+ uniforms.pointLightDistance.value = lights.point.distances;
+ uniforms.pointLightDecay.value = lights.point.decays;
+
+ uniforms.spotLightColor.value = lights.spot.colors;
+ uniforms.spotLightPosition.value = lights.spot.positions;
+ uniforms.spotLightDistance.value = lights.spot.distances;
+ uniforms.spotLightDirection.value = lights.spot.directions;
+ uniforms.spotLightAngleCos.value = lights.spot.anglesCos;
+ uniforms.spotLightExponent.value = lights.spot.exponents;
+ uniforms.spotLightDecay.value = lights.spot.decays;
+
+ uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors;
+ uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors;
+ uniforms.hemisphereLightDirection.value = lights.hemi.positions;
+
+ }
+
+ // If uniforms are marked as clean, they don't need to be loaded to the GPU.
+
+ function markUniformsLightsNeedsUpdate ( uniforms, value ) {
+
+ uniforms.ambientLightColor.needsUpdate = value;
+
+ uniforms.directionalLightColor.needsUpdate = value;
+ uniforms.directionalLightDirection.needsUpdate = value;
+
+ uniforms.pointLightColor.needsUpdate = value;
+ uniforms.pointLightPosition.needsUpdate = value;
+ uniforms.pointLightDistance.needsUpdate = value;
+ uniforms.pointLightDecay.needsUpdate = value;
+
+ uniforms.spotLightColor.needsUpdate = value;
+ uniforms.spotLightPosition.needsUpdate = value;
+ uniforms.spotLightDistance.needsUpdate = value;
+ uniforms.spotLightDirection.needsUpdate = value;
+ uniforms.spotLightAngleCos.needsUpdate = value;
+ uniforms.spotLightExponent.needsUpdate = value;
+ uniforms.spotLightDecay.needsUpdate = value;
+
+ uniforms.hemisphereLightSkyColor.needsUpdate = value;
+ uniforms.hemisphereLightGroundColor.needsUpdate = value;
+ uniforms.hemisphereLightDirection.needsUpdate = value;
+
+ }
+
+ function refreshUniformsShadow ( uniforms, lights, camera ) {
+
+ if ( uniforms.shadowMatrix ) {
+
+ var j = 0;
+
+ for ( var i = 0, il = lights.length; i < il; i ++ ) {
+
+ var light = lights[ i ];
+
+ if ( light.castShadow === true ) {
+
+ if ( light instanceof THREE.PointLight || light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) {
+
+ var shadow = light.shadow;
+
+ if ( light instanceof THREE.PointLight ) {
+
+ // for point lights we set the shadow matrix to be a translation-only matrix
+ // equal to inverse of the light's position
+ _vector3.setFromMatrixPosition( light.matrixWorld ).negate();
+ shadow.matrix.identity().setPosition( _vector3 );
+
+ // for point lights we set the sign of the shadowDarkness uniform to be negative
+ uniforms.shadowDarkness.value[ j ] = - shadow.darkness;
+
+ } else {
+
+ uniforms.shadowDarkness.value[ j ] = shadow.darkness;
+
+ }
+
+ uniforms.shadowMatrix.value[ j ] = shadow.matrix;
+ uniforms.shadowMap.value[ j ] = shadow.map;
+ uniforms.shadowMapSize.value[ j ] = shadow.mapSize;
+ uniforms.shadowBias.value[ j ] = shadow.bias;
+
+ j ++;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ // Uniforms (load to GPU)
+
+ function loadUniformsMatrices ( uniforms, object ) {
+
+ _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object.modelViewMatrix.elements );
+
+ if ( uniforms.normalMatrix ) {
+
+ _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object.normalMatrix.elements );
+
+ }
+
+ }
+
+ function getTextureUnit() {
+
+ var textureUnit = _usedTextureUnits;
+
+ if ( textureUnit >= capabilities.maxTextures ) {
+
+ console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures );
+
+ }
+
+ _usedTextureUnits += 1;
+
+ return textureUnit;
+
+ }
+
+ function loadUniformsGeneric ( uniforms ) {
+
+ var texture, textureUnit;
+
+ for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) {
+
+ var uniform = uniforms[ j ][ 0 ];
+
+ // needsUpdate property is not added to all uniforms.
+ if ( uniform.needsUpdate === false ) continue;
+
+ var type = uniform.type;
+ var value = uniform.value;
+ var location = uniforms[ j ][ 1 ];
+
+ switch ( type ) {
+
+ case '1i':
+ _gl.uniform1i( location, value );
+ break;
+
+ case '1f':
+ _gl.uniform1f( location, value );
+ break;
+
+ case '2f':
+ _gl.uniform2f( location, value[ 0 ], value[ 1 ] );
+ break;
+
+ case '3f':
+ _gl.uniform3f( location, value[ 0 ], value[ 1 ], value[ 2 ] );
+ break;
+
+ case '4f':
+ _gl.uniform4f( location, value[ 0 ], value[ 1 ], value[ 2 ], value[ 3 ] );
+ break;
+
+ case '1iv':
+ _gl.uniform1iv( location, value );
+ break;
+
+ case '3iv':
+ _gl.uniform3iv( location, value );
+ break;
+
+ case '1fv':
+ _gl.uniform1fv( location, value );
+ break;
+
+ case '2fv':
+ _gl.uniform2fv( location, value );
+ break;
+
+ case '3fv':
+ _gl.uniform3fv( location, value );
+ break;
+
+ case '4fv':
+ _gl.uniform4fv( location, value );
+ break;
+
+ case 'Matrix3fv':
+ _gl.uniformMatrix3fv( location, false, value );
+ break;
+
+ case 'Matrix4fv':
+ _gl.uniformMatrix4fv( location, false, value );
+ break;
+
+ //
+
+ case 'i':
+
+ // single integer
+ _gl.uniform1i( location, value );
+
+ break;
+
+ case 'f':
+
+ // single float
+ _gl.uniform1f( location, value );
+
+ break;
+
+ case 'v2':
+
+ // single THREE.Vector2
+ _gl.uniform2f( location, value.x, value.y );
+
+ break;
+
+ case 'v3':
+
+ // single THREE.Vector3
+ _gl.uniform3f( location, value.x, value.y, value.z );
+
+ break;
+
+ case 'v4':
+
+ // single THREE.Vector4
+ _gl.uniform4f( location, value.x, value.y, value.z, value.w );
+
+ break;
+
+ case 'c':
+
+ // single THREE.Color
+ _gl.uniform3f( location, value.r, value.g, value.b );
+
+ break;
+
+ case 'iv1':
+
+ // flat array of integers (JS or typed array)
+ _gl.uniform1iv( location, value );
+
+ break;
+
+ case 'iv':
+
+ // flat array of integers with 3 x N size (JS or typed array)
+ _gl.uniform3iv( location, value );
+
+ break;
+
+ case 'fv1':
+
+ // flat array of floats (JS or typed array)
+ _gl.uniform1fv( location, value );
+
+ break;
+
+ case 'fv':
+
+ // flat array of floats with 3 x N size (JS or typed array)
+ _gl.uniform3fv( location, value );
+
+ break;
+
+ case 'v2v':
+
+ // array of THREE.Vector2
+
+ if ( uniform._array === undefined ) {
+
+ uniform._array = new Float32Array( 2 * value.length );
+
+ }
+
+ for ( var i = 0, i2 = 0, il = value.length; i < il; i ++, i2 += 2 ) {
+
+ uniform._array[ i2 + 0 ] = value[ i ].x;
+ uniform._array[ i2 + 1 ] = value[ i ].y;
+
+ }
+
+ _gl.uniform2fv( location, uniform._array );
+
+ break;
+
+ case 'v3v':
+
+ // array of THREE.Vector3
+
+ if ( uniform._array === undefined ) {
+
+ uniform._array = new Float32Array( 3 * value.length );
+
+ }
+
+ for ( var i = 0, i3 = 0, il = value.length; i < il; i ++, i3 += 3 ) {
+
+ uniform._array[ i3 + 0 ] = value[ i ].x;
+ uniform._array[ i3 + 1 ] = value[ i ].y;
+ uniform._array[ i3 + 2 ] = value[ i ].z;
+
+ }
+
+ _gl.uniform3fv( location, uniform._array );
+
+ break;
+
+ case 'v4v':
+
+ // array of THREE.Vector4
+
+ if ( uniform._array === undefined ) {
+
+ uniform._array = new Float32Array( 4 * value.length );
+
+ }
+
+ for ( var i = 0, i4 = 0, il = value.length; i < il; i ++, i4 += 4 ) {
+
+ uniform._array[ i4 + 0 ] = value[ i ].x;
+ uniform._array[ i4 + 1 ] = value[ i ].y;
+ uniform._array[ i4 + 2 ] = value[ i ].z;
+ uniform._array[ i4 + 3 ] = value[ i ].w;
+
+ }
+
+ _gl.uniform4fv( location, uniform._array );
+
+ break;
+
+ case 'm3':
+
+ // single THREE.Matrix3
+ _gl.uniformMatrix3fv( location, false, value.elements );
+
+ break;
+
+ case 'm3v':
+
+ // array of THREE.Matrix3
+
+ if ( uniform._array === undefined ) {
+
+ uniform._array = new Float32Array( 9 * value.length );
+
+ }
+
+ for ( var i = 0, il = value.length; i < il; i ++ ) {
+
+ value[ i ].flattenToArrayOffset( uniform._array, i * 9 );
+
+ }
+
+ _gl.uniformMatrix3fv( location, false, uniform._array );
+
+ break;
+
+ case 'm4':
+
+ // single THREE.Matrix4
+ _gl.uniformMatrix4fv( location, false, value.elements );
+
+ break;
+
+ case 'm4v':
+
+ // array of THREE.Matrix4
+
+ if ( uniform._array === undefined ) {
+
+ uniform._array = new Float32Array( 16 * value.length );
+
+ }
+
+ for ( var i = 0, il = value.length; i < il; i ++ ) {
+
+ value[ i ].flattenToArrayOffset( uniform._array, i * 16 );
+
+ }
+
+ _gl.uniformMatrix4fv( location, false, uniform._array );
+
+ break;
+
+ case 't':
+
+ // single THREE.Texture (2d or cube)
+
+ texture = value;
+ textureUnit = getTextureUnit();
+
+ _gl.uniform1i( location, textureUnit );
+
+ if ( ! texture ) continue;
+
+ if ( texture instanceof THREE.CubeTexture ||
+ ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) {
+
+ // CompressedTexture can have Array in image :/
+
+ setCubeTexture( texture, textureUnit );
+
+ } else if ( texture instanceof THREE.WebGLRenderTargetCube ) {
+
+ setCubeTextureDynamic( texture.texture, textureUnit );
+
+ } else if ( texture instanceof THREE.WebGLRenderTarget ) {
+
+ _this.setTexture( texture.texture, textureUnit );
+
+ } else {
+
+ _this.setTexture( texture, textureUnit );
+
+ }
+
+ break;
+
+ case 'tv':
+
+ // array of THREE.Texture (2d or cube)
+
+ if ( uniform._array === undefined ) {
+
+ uniform._array = [];
+
+ }
+
+ for ( var i = 0, il = uniform.value.length; i < il; i ++ ) {
+
+ uniform._array[ i ] = getTextureUnit();
+
+ }
+
+ _gl.uniform1iv( location, uniform._array );
+
+ for ( var i = 0, il = uniform.value.length; i < il; i ++ ) {
+
+ texture = uniform.value[ i ];
+ textureUnit = uniform._array[ i ];
+
+ if ( ! texture ) continue;
+
+ if ( texture instanceof THREE.CubeTexture ||
+ ( texture.image instanceof Array && texture.image.length === 6 ) ) {
+
+ // CompressedTexture can have Array in image :/
+
+ setCubeTexture( texture, textureUnit );
+
+ } else if ( texture instanceof THREE.WebGLRenderTarget ) {
+
+ _this.setTexture( texture.texture, textureUnit );
+
+ } else if ( texture instanceof THREE.WebGLRenderTargetCube ) {
+
+ setCubeTextureDynamic( texture.texture, textureUnit );
+
+ } else {
+
+ _this.setTexture( texture, textureUnit );
+
+ }
+
+ }
+
+ break;
+
+ default:
+
+ console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type );
+
+ }
+
+ }
+
+ }
+
+ function setColorLinear( array, offset, color, intensity ) {
+
+ array[ offset + 0 ] = color.r * intensity;
+ array[ offset + 1 ] = color.g * intensity;
+ array[ offset + 2 ] = color.b * intensity;
+
+ }
+
+ function setupLights ( lights, camera ) {
+
+ var l, ll, light,
+ r = 0, g = 0, b = 0,
+ color, skyColor, groundColor,
+ intensity,
+ distance,
+
+ zlights = _lights,
+
+ viewMatrix = camera.matrixWorldInverse,
+
+ dirColors = zlights.directional.colors,
+ dirPositions = zlights.directional.positions,
+
+ pointColors = zlights.point.colors,
+ pointPositions = zlights.point.positions,
+ pointDistances = zlights.point.distances,
+ pointDecays = zlights.point.decays,
+
+ spotColors = zlights.spot.colors,
+ spotPositions = zlights.spot.positions,
+ spotDistances = zlights.spot.distances,
+ spotDirections = zlights.spot.directions,
+ spotAnglesCos = zlights.spot.anglesCos,
+ spotExponents = zlights.spot.exponents,
+ spotDecays = zlights.spot.decays,
+
+ hemiSkyColors = zlights.hemi.skyColors,
+ hemiGroundColors = zlights.hemi.groundColors,
+ hemiPositions = zlights.hemi.positions,
+
+ dirLength = 0,
+ pointLength = 0,
+ spotLength = 0,
+ hemiLength = 0,
+
+ dirCount = 0,
+ pointCount = 0,
+ spotCount = 0,
+ hemiCount = 0,
+
+ dirOffset = 0,
+ pointOffset = 0,
+ spotOffset = 0,
+ hemiOffset = 0;
+
+ for ( l = 0, ll = lights.length; l < ll; l ++ ) {
+
+ light = lights[ l ];
+
+ color = light.color;
+ intensity = light.intensity;
+ distance = light.distance;
+
+ if ( light instanceof THREE.AmbientLight ) {
+
+ if ( ! light.visible ) continue;
+
+ r += color.r;
+ g += color.g;
+ b += color.b;
+
+ } else if ( light instanceof THREE.DirectionalLight ) {
+
+ dirCount += 1;
+
+ if ( ! light.visible ) continue;
+
+ _direction.setFromMatrixPosition( light.matrixWorld );
+ _vector3.setFromMatrixPosition( light.target.matrixWorld );
+ _direction.sub( _vector3 );
+ _direction.transformDirection( viewMatrix );
+
+ dirOffset = dirLength * 3;
+
+ dirPositions[ dirOffset + 0 ] = _direction.x;
+ dirPositions[ dirOffset + 1 ] = _direction.y;
+ dirPositions[ dirOffset + 2 ] = _direction.z;
+
+ setColorLinear( dirColors, dirOffset, color, intensity );
+
+ dirLength += 1;
+
+ } else if ( light instanceof THREE.PointLight ) {
+
+ pointCount += 1;
+
+ if ( ! light.visible ) continue;
+
+ pointOffset = pointLength * 3;
+
+ setColorLinear( pointColors, pointOffset, color, intensity );
+
+ _vector3.setFromMatrixPosition( light.matrixWorld );
+ _vector3.applyMatrix4( viewMatrix );
+
+ pointPositions[ pointOffset + 0 ] = _vector3.x;
+ pointPositions[ pointOffset + 1 ] = _vector3.y;
+ pointPositions[ pointOffset + 2 ] = _vector3.z;
+
+ // distance is 0 if decay is 0, because there is no attenuation at all.
+ pointDistances[ pointLength ] = distance;
+ pointDecays[ pointLength ] = ( light.distance === 0 ) ? 0.0 : light.decay;
+
+ pointLength += 1;
+
+ } else if ( light instanceof THREE.SpotLight ) {
+
+ spotCount += 1;
+
+ if ( ! light.visible ) continue;
+
+ spotOffset = spotLength * 3;
+
+ setColorLinear( spotColors, spotOffset, color, intensity );
+
+ _direction.setFromMatrixPosition( light.matrixWorld );
+ _vector3.copy( _direction ).applyMatrix4( viewMatrix );
+
+ spotPositions[ spotOffset + 0 ] = _vector3.x;
+ spotPositions[ spotOffset + 1 ] = _vector3.y;
+ spotPositions[ spotOffset + 2 ] = _vector3.z;
+
+ spotDistances[ spotLength ] = distance;
+
+ _vector3.setFromMatrixPosition( light.target.matrixWorld );
+ _direction.sub( _vector3 );
+ _direction.transformDirection( viewMatrix );
+
+ spotDirections[ spotOffset + 0 ] = _direction.x;
+ spotDirections[ spotOffset + 1 ] = _direction.y;
+ spotDirections[ spotOffset + 2 ] = _direction.z;
+
+ spotAnglesCos[ spotLength ] = Math.cos( light.angle );
+ spotExponents[ spotLength ] = light.exponent;
+ spotDecays[ spotLength ] = ( light.distance === 0 ) ? 0.0 : light.decay;
+
+ spotLength += 1;
+
+ } else if ( light instanceof THREE.HemisphereLight ) {
+
+ hemiCount += 1;
+
+ if ( ! light.visible ) continue;
+
+ _direction.setFromMatrixPosition( light.matrixWorld );
+ _direction.transformDirection( viewMatrix );
+
+ hemiOffset = hemiLength * 3;
+
+ hemiPositions[ hemiOffset + 0 ] = _direction.x;
+ hemiPositions[ hemiOffset + 1 ] = _direction.y;
+ hemiPositions[ hemiOffset + 2 ] = _direction.z;
+
+ skyColor = light.color;
+ groundColor = light.groundColor;
+
+ setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity );
+ setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity );
+
+ hemiLength += 1;
+
+ }
+
+ }
+
+ // null eventual remains from removed lights
+ // (this is to avoid if in shader)
+
+ for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0;
+ for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0;
+ for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0;
+ for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0;
+ for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0;
+
+ zlights.directional.length = dirLength;
+ zlights.point.length = pointLength;
+ zlights.spot.length = spotLength;
+ zlights.hemi.length = hemiLength;
+
+ zlights.ambient[ 0 ] = r;
+ zlights.ambient[ 1 ] = g;
+ zlights.ambient[ 2 ] = b;
+
+ }
+
+ // GL state setting
+
+ this.setFaceCulling = function ( cullFace, frontFaceDirection ) {
+
+ if ( cullFace === THREE.CullFaceNone ) {
+
+ state.disable( _gl.CULL_FACE );
+
+ } else {
+
+ if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) {
+
+ _gl.frontFace( _gl.CW );
+
+ } else {
+
+ _gl.frontFace( _gl.CCW );
+
+ }
+
+ if ( cullFace === THREE.CullFaceBack ) {
+
+ _gl.cullFace( _gl.BACK );
+
+ } else if ( cullFace === THREE.CullFaceFront ) {
+
+ _gl.cullFace( _gl.FRONT );
+
+ } else {
+
+ _gl.cullFace( _gl.FRONT_AND_BACK );
+
+ }
+
+ state.enable( _gl.CULL_FACE );
+
+ }
+
+ };
+
+ // Textures
+
+ function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) {
+
+ var extension;
+
+ if ( isImagePowerOfTwo ) {
+
+ _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) );
+ _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) );
+
+ _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) );
+ _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) );
+
+ } else {
+
+ _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );
+ _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );
+
+ if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) {
+
+ console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.', texture );
+
+ }
+
+ _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );
+ _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );
+
+ if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) {
+
+ console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.', texture );
+
+ }
+
+ }
+
+ extension = extensions.get( 'EXT_texture_filter_anisotropic' );
+
+ if ( extension ) {
+
+ if ( texture.type === THREE.FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return;
+ if ( texture.type === THREE.HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return;
+
+ if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) {
+
+ _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _this.getMaxAnisotropy() ) );
+ properties.get( texture ).__currentAnisotropy = texture.anisotropy;
+
+ }
+
+ }
+
+ }
+
+ function uploadTexture( textureProperties, texture, slot ) {
+
+ if ( textureProperties.__webglInit === undefined ) {
+
+ textureProperties.__webglInit = true;
+
+ texture.addEventListener( 'dispose', onTextureDispose );
+
+ textureProperties.__webglTexture = _gl.createTexture();
+
+ _infoMemory.textures ++;
+
+ }
+
+ state.activeTexture( _gl.TEXTURE0 + slot );
+ state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );
+
+ _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
+ _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
+ _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );
+
+ texture.image = clampToMaxSize( texture.image, capabilities.maxTextureSize );
+
+ if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( texture.image ) === false ) {
+
+ texture.image = makePowerOfTwo( texture.image );
+
+ }
+
+ var image = texture.image,
+ isImagePowerOfTwo = isPowerOfTwo( image ),
+ glFormat = paramThreeToGL( texture.format ),
+ glType = paramThreeToGL( texture.type );
+
+ setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo );
+
+ var mipmap, mipmaps = texture.mipmaps;
+
+ if ( texture instanceof THREE.DataTexture ) {
+
+ // use manually created mipmaps if available
+ // if there are no manual mipmaps
+ // set 0 level mipmap and then use GL to generate other mipmap levels
+
+ if ( mipmaps.length > 0 && isImagePowerOfTwo ) {
+
+ for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+
+ mipmap = mipmaps[ i ];
+ state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
+
+ }
+
+ texture.generateMipmaps = false;
+
+ } else {
+
+ state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data );
+
+ }
+
+ } else if ( texture instanceof THREE.CompressedTexture ) {
+
+ for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+
+ mipmap = mipmaps[ i ];
+
+ if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) {
+
+ if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {
+
+ state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
+
+ } else {
+
+ console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" );
+
+ }
+
+ } else {
+
+ state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
+
+ }
+
+ }
+
+ } else {
+
+ // regular Texture (image, video, canvas)
+
+ // use manually created mipmaps if available
+ // if there are no manual mipmaps
+ // set 0 level mipmap and then use GL to generate other mipmap levels
+
+ if ( mipmaps.length > 0 && isImagePowerOfTwo ) {
+
+ for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+
+ mipmap = mipmaps[ i ];
+ state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap );
+
+ }
+
+ texture.generateMipmaps = false;
+
+ } else {
+
+ state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image );
+
+ }
+
+ }
+
+ if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );
+
+ textureProperties.__version = texture.version;
+
+ if ( texture.onUpdate ) texture.onUpdate( texture );
+
+ }
+
+ this.setTexture = function ( texture, slot ) {
+
+ var textureProperties = properties.get( texture );
+
+ if ( texture.version > 0 && textureProperties.__version !== texture.version ) {
+
+ var image = texture.image;
+
+ if ( image === undefined ) {
+
+ console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture );
+ return;
+
+ }
+
+ if ( image.complete === false ) {
+
+ console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture );
+ return;
+
+ }
+
+ uploadTexture( textureProperties, texture, slot );
+
+ return;
+
+ }
+
+ state.activeTexture( _gl.TEXTURE0 + slot );
+ state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );
+
+ };
+
+ function clampToMaxSize ( image, maxSize ) {
+
+ if ( image.width > maxSize || image.height > maxSize ) {
+
+ // Warning: Scaling through the canvas will only work with images that use
+ // premultiplied alpha.
+
+ var scale = maxSize / Math.max( image.width, image.height );
+
+ var canvas = document.createElement( 'canvas' );
+ canvas.width = Math.floor( image.width * scale );
+ canvas.height = Math.floor( image.height * scale );
+
+ var context = canvas.getContext( '2d' );
+ context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height );
+
+ console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image );
+
+ return canvas;
+
+ }
+
+ return image;
+
+ }
+
+ function isPowerOfTwo( image ) {
+
+ return THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height );
+
+ }
+
+ function textureNeedsPowerOfTwo( texture ) {
+
+ if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) return true;
+ if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) return true;
+
+ return false;
+
+ }
+
+ function makePowerOfTwo( image ) {
+
+ if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement ) {
+
+ var canvas = document.createElement( 'canvas' );
+ canvas.width = THREE.Math.nearestPowerOfTwo( image.width );
+ canvas.height = THREE.Math.nearestPowerOfTwo( image.height );
+
+ var context = canvas.getContext( '2d' );
+ context.drawImage( image, 0, 0, canvas.width, canvas.height );
+
+ console.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image );
+
+ return canvas;
+
+ }
+
+ return image;
+
+ }
+
+ function setCubeTexture ( texture, slot ) {
+
+ var textureProperties = properties.get( texture );
+
+ if ( texture.image.length === 6 ) {
+
+ if ( texture.version > 0 && textureProperties.__version !== texture.version ) {
+
+ if ( ! textureProperties.__image__webglTextureCube ) {
+
+ texture.addEventListener( 'dispose', onTextureDispose );
+
+ textureProperties.__image__webglTextureCube = _gl.createTexture();
+
+ _infoMemory.textures ++;
+
+ }
+
+ state.activeTexture( _gl.TEXTURE0 + slot );
+ state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube );
+
+ _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
+
+ var isCompressed = texture instanceof THREE.CompressedTexture;
+ var isDataTexture = texture.image[ 0 ] instanceof THREE.DataTexture;
+
+ var cubeImage = [];
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ if ( _this.autoScaleCubemaps && ! isCompressed && ! isDataTexture ) {
+
+ cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize );
+
+ } else {
+
+ cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ];
+
+ }
+
+ }
+
+ var image = cubeImage[ 0 ],
+ isImagePowerOfTwo = isPowerOfTwo( image ),
+ glFormat = paramThreeToGL( texture.format ),
+ glType = paramThreeToGL( texture.type );
+
+ setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo );
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ if ( ! isCompressed ) {
+
+ if ( isDataTexture ) {
+
+ state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data );
+
+ } else {
+
+ state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );
+
+ }
+
+ } else {
+
+ var mipmap, mipmaps = cubeImage[ i ].mipmaps;
+
+ for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {
+
+ mipmap = mipmaps[ j ];
+
+ if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) {
+
+ if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {
+
+ state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
+
+ } else {
+
+ console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setCubeTexture()" );
+
+ }
+
+ } else {
+
+ state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ if ( texture.generateMipmaps && isImagePowerOfTwo ) {
+
+ _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
+
+ }
+
+ textureProperties.__version = texture.version;
+
+ if ( texture.onUpdate ) texture.onUpdate( texture );
+
+ } else {
+
+ state.activeTexture( _gl.TEXTURE0 + slot );
+ state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube );
+
+ }
+
+ }
+
+ }
+
+ function setCubeTextureDynamic ( texture, slot ) {
+
+ state.activeTexture( _gl.TEXTURE0 + slot );
+ state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture );
+
+ }
+
+ // Render targets
+
+ function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) {
+
+ _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
+ _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 );
+
+ }
+
+ function setupRenderBuffer ( renderbuffer, renderTarget ) {
+
+ _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer );
+
+ if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {
+
+ _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height );
+ _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
+
+ /* For some reason this is not working. Defaulting to RGBA4.
+ } else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
+
+ _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height );
+ _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
+ */
+
+ } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
+
+ _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height );
+ _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
+
+ } else {
+
+ _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height );
+
+ }
+
+ }
+
+ this.setRenderTarget = function ( renderTarget ) {
+
+ var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube );
+
+ if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) {
+
+ var renderTargetProperties = properties.get( renderTarget );
+ var textureProperties = properties.get( renderTarget.texture );
+
+ if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true;
+ if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true;
+
+ renderTarget.addEventListener( 'dispose', onRenderTargetDispose );
+
+ textureProperties.__webglTexture = _gl.createTexture();
+
+ _infoMemory.textures ++;
+
+ // Setup texture, create render and frame buffers
+
+ var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ),
+ glFormat = paramThreeToGL( renderTarget.texture.format ),
+ glType = paramThreeToGL( renderTarget.texture.type );
+
+ if ( isCube ) {
+
+ renderTargetProperties.__webglFramebuffer = [];
+ renderTargetProperties.__webglRenderbuffer = [];
+
+ state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture );
+
+ setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo );
+
+ for ( var i = 0; i < 6; i ++ ) {
+
+ renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer();
+ renderTargetProperties.__webglRenderbuffer[ i ] = _gl.createRenderbuffer();
+ state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
+
+ setupFrameBuffer( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );
+ setupRenderBuffer( renderTargetProperties.__webglRenderbuffer[ i ], renderTarget );
+
+ }
+
+ if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
+
+ } else {
+
+ renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer();
+
+ if ( renderTarget.shareDepthFrom ) {
+
+ renderTargetProperties.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer;
+
+ } else {
+
+ renderTargetProperties.__webglRenderbuffer = _gl.createRenderbuffer();
+
+ }
+
+ state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );
+ setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo );
+
+ state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
+
+ setupFrameBuffer( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D );
+
+ if ( renderTarget.shareDepthFrom ) {
+
+ if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {
+
+ _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTargetProperties.__webglRenderbuffer );
+
+ } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
+
+ _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTargetProperties.__webglRenderbuffer );
+
+ }
+
+ } else {
+
+ setupRenderBuffer( renderTargetProperties.__webglRenderbuffer, renderTarget );
+
+ }
+
+ if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );
+
+ }
+
+ // Release everything
+
+ if ( isCube ) {
+
+ state.bindTexture( _gl.TEXTURE_CUBE_MAP, null );
+
+ } else {
+
+ state.bindTexture( _gl.TEXTURE_2D, null );
+
+ }
+
+ _gl.bindRenderbuffer( _gl.RENDERBUFFER, null );
+ _gl.bindFramebuffer( _gl.FRAMEBUFFER, null );
+
+ }
+
+ var framebuffer, width, height, vx, vy;
+
+ if ( renderTarget ) {
+
+ var renderTargetProperties = properties.get( renderTarget );
+
+ if ( isCube ) {
+
+ framebuffer = renderTargetProperties.__webglFramebuffer[ renderTarget.activeCubeFace ];
+
+ } else {
+
+ framebuffer = renderTargetProperties.__webglFramebuffer;
+
+ }
+
+ width = renderTarget.width;
+ height = renderTarget.height;
+
+ vx = 0;
+ vy = 0;
+
+ } else {
+
+ framebuffer = null;
+
+ width = _viewportWidth;
+ height = _viewportHeight;
+
+ vx = _viewportX;
+ vy = _viewportY;
+
+ }
+
+ if ( framebuffer !== _currentFramebuffer ) {
+
+ _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
+ _gl.viewport( vx, vy, width, height );
+
+ _currentFramebuffer = framebuffer;
+
+ }
+
+ if ( isCube ) {
+
+ var textureProperties = properties.get( renderTarget.texture );
+ _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, 0 );
+
+ }
+
+ _currentWidth = width;
+ _currentHeight = height;
+
+ };
+
+ this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) {
+
+ if ( renderTarget instanceof THREE.WebGLRenderTarget === false ) {
+
+ console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' );
+ return;
+
+ }
+
+ var framebuffer = properties.get( renderTarget ).__webglFramebuffer;
+
+ if ( framebuffer ) {
+
+ var restore = false;
+
+ if ( framebuffer !== _currentFramebuffer ) {
+
+ _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
+
+ restore = true;
+
+ }
+
+ try {
+
+ var texture = renderTarget.texture;
+
+ if ( texture.format !== THREE.RGBAFormat
+ && paramThreeToGL( texture.format ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) {
+
+ console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' );
+ return;
+
+ }
+
+ if ( texture.type !== THREE.UnsignedByteType
+ && paramThreeToGL( texture.type ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE )
+ && ! ( texture.type === THREE.FloatType && extensions.get( 'WEBGL_color_buffer_float' ) )
+ && ! ( texture.type === THREE.HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) {
+
+ console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' );
+ return;
+
+ }
+
+ if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) {
+
+ _gl.readPixels( x, y, width, height, paramThreeToGL( texture.format ), paramThreeToGL( texture.type ), buffer );
+
+ } else {
+
+ console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' );
+
+ }
+
+ } finally {
+
+ if ( restore ) {
+
+ _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer );
+
+ }
+
+ }
+
+ }
+
+ };
+
+ function updateRenderTargetMipmap( renderTarget ) {
+
+ var target = renderTarget instanceof THREE.WebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D;
+ var texture = properties.get( renderTarget.texture ).__webglTexture;
+
+ state.bindTexture( target, texture );
+ _gl.generateMipmap( target );
+ state.bindTexture( target, null );
+
+ }
+
+ // Fallback filters for non-power-of-2 textures
+
+ function filterFallback ( f ) {
+
+ if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) {
+
+ return _gl.NEAREST;
+
+ }
+
+ return _gl.LINEAR;
+
+ }
+
+ // Map three.js constants to WebGL constants
+
+ function paramThreeToGL ( p ) {
+
+ var extension;
+
+ if ( p === THREE.RepeatWrapping ) return _gl.REPEAT;
+ if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE;
+ if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT;
+
+ if ( p === THREE.NearestFilter ) return _gl.NEAREST;
+ if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST;
+ if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR;
+
+ if ( p === THREE.LinearFilter ) return _gl.LINEAR;
+ if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST;
+ if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR;
+
+ if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE;
+ if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4;
+ if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1;
+ if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5;
+
+ if ( p === THREE.ByteType ) return _gl.BYTE;
+ if ( p === THREE.ShortType ) return _gl.SHORT;
+ if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT;
+ if ( p === THREE.IntType ) return _gl.INT;
+ if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT;
+ if ( p === THREE.FloatType ) return _gl.FLOAT;
+
+ extension = extensions.get( 'OES_texture_half_float' );
+
+ if ( extension !== null ) {
+
+ if ( p === THREE.HalfFloatType ) return extension.HALF_FLOAT_OES;
+
+ }
+
+ if ( p === THREE.AlphaFormat ) return _gl.ALPHA;
+ if ( p === THREE.RGBFormat ) return _gl.RGB;
+ if ( p === THREE.RGBAFormat ) return _gl.RGBA;
+ if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE;
+ if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA;
+
+ if ( p === THREE.AddEquation ) return _gl.FUNC_ADD;
+ if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT;
+ if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT;
+
+ if ( p === THREE.ZeroFactor ) return _gl.ZERO;
+ if ( p === THREE.OneFactor ) return _gl.ONE;
+ if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR;
+ if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR;
+ if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA;
+ if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA;
+ if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA;
+ if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA;
+
+ if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR;
+ if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR;
+ if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE;
+
+ extension = extensions.get( 'WEBGL_compressed_texture_s3tc' );
+
+ if ( extension !== null ) {
+
+ if ( p === THREE.RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;
+ if ( p === THREE.RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
+ if ( p === THREE.RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;
+ if ( p === THREE.RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;
+
+ }
+
+ extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' );
+
+ if ( extension !== null ) {
+
+ if ( p === THREE.RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
+ if ( p === THREE.RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
+ if ( p === THREE.RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
+ if ( p === THREE.RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
+
+ }
+
+ extension = extensions.get( 'EXT_blend_minmax' );
+
+ if ( extension !== null ) {
+
+ if ( p === THREE.MinEquation ) return extension.MIN_EXT;
+ if ( p === THREE.MaxEquation ) return extension.MAX_EXT;
+
+ }
+
+ return 0;
+
+ }
+
+ // DEPRECATED
+
+ this.supportsFloatTextures = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' );
+ return extensions.get( 'OES_texture_float' );
+
+ };
+
+ this.supportsHalfFloatTextures = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' );
+ return extensions.get( 'OES_texture_half_float' );
+
+ };
+
+ this.supportsStandardDerivatives = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' );
+ return extensions.get( 'OES_standard_derivatives' );
+
+ };
+
+ this.supportsCompressedTextureS3TC = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' );
+ return extensions.get( 'WEBGL_compressed_texture_s3tc' );
+
+ };
+
+ this.supportsCompressedTexturePVRTC = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' );
+ return extensions.get( 'WEBGL_compressed_texture_pvrtc' );
+
+ };
+
+ this.supportsBlendMinMax = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' );
+ return extensions.get( 'EXT_blend_minmax' );
+
+ };
+
+ this.supportsVertexTextures = function () {
+
+ return capabilities.vertexTextures;
+
+ };
+
+ this.supportsInstancedArrays = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' );
+ return extensions.get( 'ANGLE_instanced_arrays' );
+
+ };
+
+ //
+
+ this.initMaterial = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' );
+
+ };
+
+ this.addPrePlugin = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' );
+
+ };
+
+ this.addPostPlugin = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' );
+
+ };
+
+ this.updateShadowMap = function () {
+
+ console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' );
+
+ };
+
+ Object.defineProperties( this, {
+ shadowMapEnabled: {
+ get: function () {
+
+ return shadowMap.enabled;
+
+ },
+ set: function ( value ) {
+
+ console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' );
+ shadowMap.enabled = value;
+
+ }
+ },
+ shadowMapType: {
+ get: function () {
+
+ return shadowMap.type;
+
+ },
+ set: function ( value ) {
+
+ console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' );
+ shadowMap.type = value;
+
+ }
+ },
+ shadowMapCullFace: {
+ get: function () {
+
+ return shadowMap.cullFace;
+
+ },
+ set: function ( value ) {
+
+ console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace.' );
+ shadowMap.cullFace = value;
+
+ }
+ },
+ shadowMapDebug: {
+ get: function () {
+
+ return shadowMap.debug;
+
+ },
+ set: function ( value ) {
+
+ console.warn( 'THREE.WebGLRenderer: .shadowMapDebug is now .shadowMap.debug.' );
+ shadowMap.debug = value;
+
+ }
+ }
+ } );
+
+};
+
+// File:src/renderers/WebGLRenderTarget.js
+
+/**
+ * @author szimek / https://github.com/szimek/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.WebGLRenderTarget = function ( width, height, options ) {
+
+ this.uuid = THREE.Math.generateUUID();
+
+ this.width = width;
+ this.height = height;
+
+ options = options || {};
+
+ if ( options.minFilter === undefined ) options.minFilter = THREE.LinearFilter;
+
+ this.texture = new THREE.Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy );
+
+ this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
+ this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true;
+
+ this.shareDepthFrom = options.shareDepthFrom !== undefined ? options.shareDepthFrom : null;
+
+};
+
+THREE.WebGLRenderTarget.prototype = {
+
+ constructor: THREE.WebGLRenderTarget,
+
+ get wrapS() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' );
+
+ return this.texture.wrapS;
+
+ },
+
+ set wrapS( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' );
+
+ this.texture.wrapS = value;
+
+ },
+
+ get wrapT() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' );
+
+ return this.texture.wrapT;
+
+ },
+
+ set wrapT( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' );
+
+ this.texture.wrapT = value;
+
+ },
+
+ get magFilter() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' );
+
+ return this.texture.magFilter;
+
+ },
+
+ set magFilter( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' );
+
+ this.texture.magFilter = value;
+
+ },
+
+ get minFilter() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' );
+
+ return this.texture.minFilter;
+
+ },
+
+ set minFilter( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' );
+
+ this.texture.minFilter = value;
+
+ },
+
+ get anisotropy() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' );
+
+ return this.texture.anisotropy;
+
+ },
+
+ set anisotropy( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' );
+
+ this.texture.anisotropy = value;
+
+ },
+
+ get offset() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' );
+
+ return this.texture.offset;
+
+ },
+
+ set offset( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' );
+
+ this.texture.offset = value;
+
+ },
+
+ get repeat() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' );
+
+ return this.texture.repeat;
+
+ },
+
+ set repeat( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' );
+
+ this.texture.repeat = value;
+
+ },
+
+ get format() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' );
+
+ return this.texture.format;
+
+ },
+
+ set format( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' );
+
+ this.texture.format = value;
+
+ },
+
+ get type() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' );
+
+ return this.texture.type;
+
+ },
+
+ set type( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' );
+
+ this.texture.type = value;
+
+ },
+
+ get generateMipmaps() {
+
+ console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' );
+
+ return this.texture.generateMipmaps;
+
+ },
+
+ set generateMipmaps( value ) {
+
+ console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' );
+
+ this.texture.generateMipmaps = value;
+
+ },
+
+ //
+
+ setSize: function ( width, height ) {
+
+ if ( this.width !== width || this.height !== height ) {
+
+ this.width = width;
+ this.height = height;
+
+ this.dispose();
+
+ }
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.width = source.width;
+ this.height = source.height;
+
+ this.texture = source.texture.clone();
+
+ this.depthBuffer = source.depthBuffer;
+ this.stencilBuffer = source.stencilBuffer;
+
+ this.shareDepthFrom = source.shareDepthFrom;
+
+ return this;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ }
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.WebGLRenderTarget.prototype );
+
+// File:src/renderers/WebGLRenderTargetCube.js
+
+/**
+ * @author alteredq / http://alteredqualia.com
+ */
+
+THREE.WebGLRenderTargetCube = function ( width, height, options ) {
+
+ THREE.WebGLRenderTarget.call( this, width, height, options );
+
+ this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5
+
+};
+
+THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype );
+THREE.WebGLRenderTargetCube.prototype.constructor = THREE.WebGLRenderTargetCube;
+
+// File:src/renderers/webgl/WebGLBufferRenderer.js
+
+/**
+* @author mrdoob / http://mrdoob.com/
+*/
+
+THREE.WebGLBufferRenderer = function ( _gl, extensions, _infoRender ) {
+
+ var mode;
+
+ function setMode( value ) {
+
+ mode = value;
+
+ }
+
+ function render( start, count ) {
+
+ _gl.drawArrays( mode, start, count );
+
+ _infoRender.calls ++;
+ _infoRender.vertices += count;
+ if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3;
+
+ }
+
+ function renderInstances( geometry ) {
+
+ var extension = extensions.get( 'ANGLE_instanced_arrays' );
+
+ if ( extension === null ) {
+
+ console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
+ return;
+
+ }
+
+ var position = geometry.attributes.position;
+
+ if ( position instanceof THREE.InterleavedBufferAttribute ) {
+
+ extension.drawArraysInstancedANGLE( mode, 0, position.data.count, geometry.maxInstancedCount );
+
+ } else {
+
+ extension.drawArraysInstancedANGLE( mode, 0, position.count, geometry.maxInstancedCount );
+
+ }
+
+ }
+
+ this.setMode = setMode;
+ this.render = render;
+ this.renderInstances = renderInstances;
+
+};
+
+// File:src/renderers/webgl/WebGLIndexedBufferRenderer.js
+
+/**
+* @author mrdoob / http://mrdoob.com/
+*/
+
+THREE.WebGLIndexedBufferRenderer = function ( _gl, extensions, _infoRender ) {
+
+ var mode;
+
+ function setMode( value ) {
+
+ mode = value;
+
+ }
+
+ var type, size;
+
+ function setIndex( index ) {
+
+ if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) {
+
+ type = _gl.UNSIGNED_INT;
+ size = 4;
+
+ } else {
+
+ type = _gl.UNSIGNED_SHORT;
+ size = 2;
+
+ }
+
+ }
+
+ function render( start, count ) {
+
+ _gl.drawElements( mode, count, type, start * size );
+
+ _infoRender.calls ++;
+ _infoRender.vertices += count;
+ if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3;
+
+ }
+
+ function renderInstances( geometry ) {
+
+ var extension = extensions.get( 'ANGLE_instanced_arrays' );
+
+ if ( extension === null ) {
+
+ console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
+ return;
+
+ }
+
+ var index = geometry.index;
+
+ extension.drawElementsInstancedANGLE( mode, index.array.length, type, 0, geometry.maxInstancedCount );
+
+ }
+
+ this.setMode = setMode;
+ this.setIndex = setIndex;
+ this.render = render;
+ this.renderInstances = renderInstances;
+
+};
+
+// File:src/renderers/webgl/WebGLExtensions.js
+
+/**
+* @author mrdoob / http://mrdoob.com/
+*/
+
+THREE.WebGLExtensions = function ( gl ) {
+
+ var extensions = {};
+
+ this.get = function ( name ) {
+
+ if ( extensions[ name ] !== undefined ) {
+
+ return extensions[ name ];
+
+ }
+
+ var extension;
+
+ switch ( name ) {
+
+ case 'EXT_texture_filter_anisotropic':
+ extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );
+ break;
+
+ case 'WEBGL_compressed_texture_s3tc':
+ extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );
+ break;
+
+ case 'WEBGL_compressed_texture_pvrtc':
+ extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );
+ break;
+
+ default:
+ extension = gl.getExtension( name );
+
+ }
+
+ if ( extension === null ) {
+
+ console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' );
+
+ }
+
+ extensions[ name ] = extension;
+
+ return extension;
+
+ };
+
+};
+
+// File:src/renderers/webgl/WebGLCapabilities.js
+
+THREE.WebGLCapabilities = function ( gl, extensions, parameters ) {
+
+ function getMaxPrecision( precision ) {
+
+ if ( precision === 'highp' ) {
+
+ if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 &&
+ gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) {
+
+ return 'highp';
+
+ }
+
+ precision = 'mediump';
+
+ }
+
+ if ( precision === 'mediump' ) {
+
+ if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 &&
+ gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) {
+
+ return 'mediump';
+
+ }
+
+ }
+
+ return 'lowp';
+
+ }
+
+ this.getMaxPrecision = getMaxPrecision;
+
+ this.precision = parameters.precision !== undefined ? parameters.precision : 'highp',
+ this.logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false;
+
+ this.maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );
+ this.maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );
+ this.maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE );
+ this.maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE );
+
+ this.maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );
+ this.maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS );
+ this.maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS );
+ this.maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS );
+
+ this.vertexTextures = this.maxVertexTextures > 0;
+ this.floatFragmentTextures = !! extensions.get( 'OES_texture_float' );
+ this.floatVertexTextures = this.vertexTextures && this.floatFragmentTextures;
+
+ var _maxPrecision = getMaxPrecision( this.precision );
+
+ if ( _maxPrecision !== this.precision ) {
+
+ console.warn( 'THREE.WebGLRenderer:', this.precision, 'not supported, using', _maxPrecision, 'instead.' );
+ this.precision = _maxPrecision;
+
+ }
+
+ if ( this.logarithmicDepthBuffer ) {
+
+ this.logarithmicDepthBuffer = !! extensions.get( 'EXT_frag_depth' );
+
+ }
+
+};
+
+// File:src/renderers/webgl/WebGLGeometries.js
+
+/**
+* @author mrdoob / http://mrdoob.com/
+*/
+
+THREE.WebGLGeometries = function ( gl, properties, info ) {
+
+ var geometries = {};
+
+ function get( object ) {
+
+ var geometry = object.geometry;
+
+ if ( geometries[ geometry.id ] !== undefined ) {
+
+ return geometries[ geometry.id ];
+
+ }
+
+ geometry.addEventListener( 'dispose', onGeometryDispose );
+
+ var buffergeometry;
+
+ if ( geometry instanceof THREE.BufferGeometry ) {
+
+ buffergeometry = geometry;
+
+ } else if ( geometry instanceof THREE.Geometry ) {
+
+ if ( geometry._bufferGeometry === undefined ) {
+
+ geometry._bufferGeometry = new THREE.BufferGeometry().setFromObject( object );
+
+ }
+
+ buffergeometry = geometry._bufferGeometry;
+
+ }
+
+ geometries[ geometry.id ] = buffergeometry;
+
+ info.memory.geometries ++;
+
+ return buffergeometry;
+
+ }
+
+ function onGeometryDispose( event ) {
+
+ var geometry = event.target;
+ var buffergeometry = geometries[ geometry.id ];
+
+ deleteAttributes( buffergeometry.attributes );
+
+ geometry.removeEventListener( 'dispose', onGeometryDispose );
+
+ delete geometries[ geometry.id ];
+
+ var property = properties.get( geometry );
+ if ( property.wireframe ) deleteAttribute( property.wireframe );
+
+ info.memory.geometries --;
+
+ }
+
+ function getAttributeBuffer( attribute ) {
+
+ if ( attribute instanceof THREE.InterleavedBufferAttribute ) {
+
+ return properties.get( attribute.data ).__webglBuffer;
+
+ }
+
+ return properties.get( attribute ).__webglBuffer;
+
+ }
+
+ function deleteAttribute( attribute ) {
+
+ var buffer = getAttributeBuffer( attribute );
+
+ if ( buffer !== undefined ) {
+
+ gl.deleteBuffer( buffer );
+ removeAttributeBuffer( attribute );
+
+ }
+
+ }
+
+ function deleteAttributes( attributes ) {
+
+ for ( var name in attributes ) {
+
+ deleteAttribute( attributes[ name ] );
+
+ }
+
+ }
+
+ function removeAttributeBuffer( attribute ) {
+
+ if ( attribute instanceof THREE.InterleavedBufferAttribute ) {
+
+ properties.delete( attribute.data );
+
+ } else {
+
+ properties.delete( attribute );
+
+ }
+
+ }
+
+ this.get = get;
+
+};
+
+// File:src/renderers/webgl/WebGLObjects.js
+
+/**
+* @author mrdoob / http://mrdoob.com/
+*/
+
+THREE.WebGLObjects = function ( gl, properties, info ) {
+
+ var geometries = new THREE.WebGLGeometries( gl, properties, info );
+
+ //
+
+ function update( object ) {
+
+ // TODO: Avoid updating twice (when using shadowMap). Maybe add frame counter.
+
+ var geometry = geometries.get( object );
+
+ if ( object.geometry instanceof THREE.Geometry ) {
+
+ geometry.updateFromObject( object );
+
+ }
+
+ var index = geometry.index;
+ var attributes = geometry.attributes;
+
+ if ( index !== null ) {
+
+ updateAttribute( index, gl.ELEMENT_ARRAY_BUFFER );
+
+ }
+
+ for ( var name in attributes ) {
+
+ updateAttribute( attributes[ name ], gl.ARRAY_BUFFER );
+
+ }
+
+ // morph targets
+
+ var morphAttributes = geometry.morphAttributes;
+
+ for ( var name in morphAttributes ) {
+
+ var array = morphAttributes[ name ];
+
+ for ( var i = 0, l = array.length; i < l; i ++ ) {
+
+ updateAttribute( array[ i ], gl.ARRAY_BUFFER );
+
+ }
+
+ }
+
+ return geometry;
+
+ }
+
+ function updateAttribute( attribute, bufferType ) {
+
+ var data = ( attribute instanceof THREE.InterleavedBufferAttribute ) ? attribute.data : attribute;
+
+ var attributeProperties = properties.get( data );
+
+ if ( attributeProperties.__webglBuffer === undefined ) {
+
+ createBuffer( attributeProperties, data, bufferType );
+
+ } else if ( attributeProperties.version !== data.version ) {
+
+ updateBuffer( attributeProperties, data, bufferType );
+
+ }
+
+ }
+
+ function createBuffer( attributeProperties, data, bufferType ) {
+
+ attributeProperties.__webglBuffer = gl.createBuffer();
+ gl.bindBuffer( bufferType, attributeProperties.__webglBuffer );
+
+ var usage = data.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW;
+
+ gl.bufferData( bufferType, data.array, usage );
+
+ attributeProperties.version = data.version;
+
+ }
+
+ function updateBuffer( attributeProperties, data, bufferType ) {
+
+ gl.bindBuffer( bufferType, attributeProperties.__webglBuffer );
+
+ if ( data.dynamic === false || data.updateRange.count === - 1 ) {
+
+ // Not using update ranges
+
+ gl.bufferSubData( bufferType, 0, data.array );
+
+ } else if ( data.updateRange.count === 0 ) {
+
+ console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' );
+
+ } else {
+
+ gl.bufferSubData( bufferType, data.updateRange.offset * data.array.BYTES_PER_ELEMENT,
+ data.array.subarray( data.updateRange.offset, data.updateRange.offset + data.updateRange.count ) );
+
+ data.updateRange.count = 0; // reset range
+
+ }
+
+ attributeProperties.version = data.version;
+
+ }
+
+ function getAttributeBuffer( attribute ) {
+
+ if ( attribute instanceof THREE.InterleavedBufferAttribute ) {
+
+ return properties.get( attribute.data ).__webglBuffer;
+
+ }
+
+ return properties.get( attribute ).__webglBuffer;
+
+ }
+
+ function getWireframeAttribute( geometry ) {
+
+ var property = properties.get( geometry );
+
+ if ( property.wireframe !== undefined ) {
+
+ return property.wireframe;
+
+ }
+
+ var indices = [];
+
+ var index = geometry.index;
+ var attributes = geometry.attributes;
+ var position = attributes.position;
+
+ // console.time( 'wireframe' );
+
+ if ( index !== null ) {
+
+ var edges = {};
+ var array = index.array;
+
+ for ( var i = 0, l = array.length; i < l; i += 3 ) {
+
+ var a = array[ i + 0 ];
+ var b = array[ i + 1 ];
+ var c = array[ i + 2 ];
+
+ if ( checkEdge( edges, a, b ) ) indices.push( a, b );
+ if ( checkEdge( edges, b, c ) ) indices.push( b, c );
+ if ( checkEdge( edges, c, a ) ) indices.push( c, a );
+
+ }
+
+ } else {
+
+ var array = attributes.position.array;
+
+ for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) {
+
+ var a = i + 0;
+ var b = i + 1;
+ var c = i + 2;
+
+ indices.push( a, b, b, c, c, a );
+
+ }
+
+ }
+
+ // console.timeEnd( 'wireframe' );
+
+ var TypeArray = position.count > 65535 ? Uint32Array : Uint16Array;
+ var attribute = new THREE.BufferAttribute( new TypeArray( indices ), 1 );
+
+ updateAttribute( attribute, gl.ELEMENT_ARRAY_BUFFER );
+
+ property.wireframe = attribute;
+
+ return attribute;
+
+ }
+
+ function checkEdge( edges, a, b ) {
+
+ if ( a > b ) {
+
+ var tmp = a;
+ a = b;
+ b = tmp;
+
+ }
+
+ var list = edges[ a ];
+
+ if ( list === undefined ) {
+
+ edges[ a ] = [ b ];
+ return true;
+
+ } else if ( list.indexOf( b ) === -1 ) {
+
+ list.push( b );
+ return true;
+
+ }
+
+ return false;
+
+ }
+
+ this.getAttributeBuffer = getAttributeBuffer;
+ this.getWireframeAttribute = getWireframeAttribute;
+
+ this.update = update;
+
+};
+
+// File:src/renderers/webgl/WebGLProgram.js
+
+THREE.WebGLProgram = ( function () {
+
+ var programIdCount = 0;
+
+ function generateDefines( defines ) {
+
+ var chunks = [];
+
+ for ( var name in defines ) {
+
+ var value = defines[ name ];
+
+ if ( value === false ) continue;
+
+ chunks.push( '#define ' + name + ' ' + value );
+
+ }
+
+ return chunks.join( '\n' );
+
+ }
+
+ function fetchUniformLocations( gl, program, identifiers ) {
+
+ var uniforms = {};
+
+ var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );
+
+ for ( var i = 0; i < n; i ++ ) {
+
+ var info = gl.getActiveUniform( program, i );
+ var name = info.name;
+ var location = gl.getUniformLocation( program, name );
+
+ // console.log("THREE.WebGLProgram: ACTIVE UNIFORM:", name);
+
+ var suffixPos = name.lastIndexOf( '[0]' );
+ if ( suffixPos !== - 1 && suffixPos === name.length - 3 ) {
+
+ uniforms[ name.substr( 0, suffixPos ) ] = location;
+
+ }
+
+ uniforms[ name ] = location;
+
+ }
+
+ return uniforms;
+
+ }
+
+ function fetchAttributeLocations( gl, program, identifiers ) {
+
+ var attributes = {};
+
+ var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES );
+
+ for ( var i = 0; i < n; i ++ ) {
+
+ var info = gl.getActiveAttrib( program, i );
+ var name = info.name;
+
+ // console.log("THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i );
+
+ attributes[ name ] = gl.getAttribLocation( program, name );
+
+ }
+
+ return attributes;
+
+ }
+
+ function filterEmptyLine( string ) {
+
+ return string !== '';
+
+ }
+
+ return function WebGLProgram( renderer, code, material, parameters ) {
+
+ var gl = renderer.context;
+
+ var defines = material.defines;
+
+ var vertexShader = material.__webglShader.vertexShader;
+ var fragmentShader = material.__webglShader.fragmentShader;
+
+ var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC';
+
+ if ( parameters.shadowMapType === THREE.PCFShadowMap ) {
+
+ shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';
+
+ } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) {
+
+ shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';
+
+ }
+
+ var envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
+ var envMapModeDefine = 'ENVMAP_MODE_REFLECTION';
+ var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';
+
+ if ( parameters.envMap ) {
+
+ switch ( material.envMap.mapping ) {
+
+ case THREE.CubeReflectionMapping:
+ case THREE.CubeRefractionMapping:
+ envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
+ break;
+
+ case THREE.EquirectangularReflectionMapping:
+ case THREE.EquirectangularRefractionMapping:
+ envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC';
+ break;
+
+ case THREE.SphericalReflectionMapping:
+ envMapTypeDefine = 'ENVMAP_TYPE_SPHERE';
+ break;
+
+ }
+
+ switch ( material.envMap.mapping ) {
+
+ case THREE.CubeRefractionMapping:
+ case THREE.EquirectangularRefractionMapping:
+ envMapModeDefine = 'ENVMAP_MODE_REFRACTION';
+ break;
+
+ }
+
+ switch ( material.combine ) {
+
+ case THREE.MultiplyOperation:
+ envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';
+ break;
+
+ case THREE.MixOperation:
+ envMapBlendingDefine = 'ENVMAP_BLENDING_MIX';
+ break;
+
+ case THREE.AddOperation:
+ envMapBlendingDefine = 'ENVMAP_BLENDING_ADD';
+ break;
+
+ }
+
+ }
+
+ var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0;
+
+ // console.log( 'building new program ' );
+
+ //
+
+ var customDefines = generateDefines( defines );
+
+ //
+
+ var program = gl.createProgram();
+
+ var prefixVertex, prefixFragment;
+
+ if ( material instanceof THREE.RawShaderMaterial ) {
+
+ prefixVertex = '';
+ prefixFragment = '';
+
+ } else {
+
+ prefixVertex = [
+
+ 'precision ' + parameters.precision + ' float;',
+ 'precision ' + parameters.precision + ' int;',
+
+ '#define SHADER_NAME ' + material.__webglShader.name,
+
+ customDefines,
+
+ parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '',
+
+ renderer.gammaInput ? '#define GAMMA_INPUT' : '',
+ renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '',
+ '#define GAMMA_FACTOR ' + gammaFactorDefine,
+
+ '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights,
+ '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights,
+ '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights,
+ '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights,
+
+ '#define MAX_SHADOWS ' + parameters.maxShadows,
+
+ '#define MAX_BONES ' + parameters.maxBones,
+
+ parameters.map ? '#define USE_MAP' : '',
+ parameters.envMap ? '#define USE_ENVMAP' : '',
+ parameters.envMap ? '#define ' + envMapModeDefine : '',
+ parameters.lightMap ? '#define USE_LIGHTMAP' : '',
+ parameters.aoMap ? '#define USE_AOMAP' : '',
+ parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
+ parameters.bumpMap ? '#define USE_BUMPMAP' : '',
+ parameters.normalMap ? '#define USE_NORMALMAP' : '',
+ parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '',
+ parameters.specularMap ? '#define USE_SPECULARMAP' : '',
+ parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
+ parameters.vertexColors ? '#define USE_COLOR' : '',
+
+ parameters.flatShading ? '#define FLAT_SHADED' : '',
+
+ parameters.skinning ? '#define USE_SKINNING' : '',
+ parameters.useVertexTexture ? '#define BONE_TEXTURE' : '',
+
+ parameters.morphTargets ? '#define USE_MORPHTARGETS' : '',
+ parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '',
+ parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
+ parameters.flipSided ? '#define FLIP_SIDED' : '',
+
+ parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
+ parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
+ parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '',
+ parameters.pointLightShadows > 0 ? '#define POINT_LIGHT_SHADOWS' : '',
+
+ parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',
+
+ parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
+ parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',
+
+
+ 'uniform mat4 modelMatrix;',
+ 'uniform mat4 modelViewMatrix;',
+ 'uniform mat4 projectionMatrix;',
+ 'uniform mat4 viewMatrix;',
+ 'uniform mat3 normalMatrix;',
+ 'uniform vec3 cameraPosition;',
+
+ 'attribute vec3 position;',
+ 'attribute vec3 normal;',
+ 'attribute vec2 uv;',
+
+ '#ifdef USE_COLOR',
+
+ ' attribute vec3 color;',
+
+ '#endif',
+
+ '#ifdef USE_MORPHTARGETS',
+
+ ' attribute vec3 morphTarget0;',
+ ' attribute vec3 morphTarget1;',
+ ' attribute vec3 morphTarget2;',
+ ' attribute vec3 morphTarget3;',
+
+ ' #ifdef USE_MORPHNORMALS',
+
+ ' attribute vec3 morphNormal0;',
+ ' attribute vec3 morphNormal1;',
+ ' attribute vec3 morphNormal2;',
+ ' attribute vec3 morphNormal3;',
+
+ ' #else',
+
+ ' attribute vec3 morphTarget4;',
+ ' attribute vec3 morphTarget5;',
+ ' attribute vec3 morphTarget6;',
+ ' attribute vec3 morphTarget7;',
+
+ ' #endif',
+
+ '#endif',
+
+ '#ifdef USE_SKINNING',
+
+ ' attribute vec4 skinIndex;',
+ ' attribute vec4 skinWeight;',
+
+ '#endif',
+
+ '\n'
+
+ ].filter( filterEmptyLine ).join( '\n' );
+
+ prefixFragment = [
+
+ parameters.bumpMap || parameters.normalMap || parameters.flatShading || material.derivatives ? '#extension GL_OES_standard_derivatives : enable' : '',
+ parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '',
+
+ 'precision ' + parameters.precision + ' float;',
+ 'precision ' + parameters.precision + ' int;',
+
+ '#define SHADER_NAME ' + material.__webglShader.name,
+
+ customDefines,
+
+ '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights,
+ '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights,
+ '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights,
+ '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights,
+
+ '#define MAX_SHADOWS ' + parameters.maxShadows,
+
+ parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '',
+
+ renderer.gammaInput ? '#define GAMMA_INPUT' : '',
+ renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '',
+ '#define GAMMA_FACTOR ' + gammaFactorDefine,
+
+ ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',
+ ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',
+
+ parameters.map ? '#define USE_MAP' : '',
+ parameters.envMap ? '#define USE_ENVMAP' : '',
+ parameters.envMap ? '#define ' + envMapTypeDefine : '',
+ parameters.envMap ? '#define ' + envMapModeDefine : '',
+ parameters.envMap ? '#define ' + envMapBlendingDefine : '',
+ parameters.lightMap ? '#define USE_LIGHTMAP' : '',
+ parameters.aoMap ? '#define USE_AOMAP' : '',
+ parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
+ parameters.bumpMap ? '#define USE_BUMPMAP' : '',
+ parameters.normalMap ? '#define USE_NORMALMAP' : '',
+ parameters.specularMap ? '#define USE_SPECULARMAP' : '',
+ parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
+ parameters.vertexColors ? '#define USE_COLOR' : '',
+
+ parameters.flatShading ? '#define FLAT_SHADED' : '',
+
+ parameters.metal ? '#define METAL' : '',
+ parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
+ parameters.flipSided ? '#define FLIP_SIDED' : '',
+
+ parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
+ parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
+ parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '',
+ parameters.pointLightShadows > 0 ? '#define POINT_LIGHT_SHADOWS' : '',
+
+ parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
+ parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',
+
+ 'uniform mat4 viewMatrix;',
+ 'uniform vec3 cameraPosition;',
+
+ '\n'
+
+ ].filter( filterEmptyLine ).join( '\n' );
+
+ }
+
+ var vertexGlsl = prefixVertex + vertexShader;
+ var fragmentGlsl = prefixFragment + fragmentShader;
+
+ var glVertexShader = THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );
+ var glFragmentShader = THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );
+
+ gl.attachShader( program, glVertexShader );
+ gl.attachShader( program, glFragmentShader );
+
+ // Force a particular attribute to index 0.
+
+ if ( material.index0AttributeName !== undefined ) {
+
+ gl.bindAttribLocation( program, 0, material.index0AttributeName );
+
+ } else if ( parameters.morphTargets === true ) {
+
+ // programs with morphTargets displace position out of attribute 0
+ gl.bindAttribLocation( program, 0, 'position' );
+
+ }
+
+ gl.linkProgram( program );
+
+ var programLog = gl.getProgramInfoLog( program );
+ var vertexLog = gl.getShaderInfoLog( glVertexShader );
+ var fragmentLog = gl.getShaderInfoLog( glFragmentShader );
+
+ var runnable = true;
+ var haveDiagnostics = true;
+
+ if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) {
+
+ runnable = false;
+
+ console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog );
+
+ } else if ( programLog !== '' ) {
+
+ console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog );
+
+ } else if ( vertexLog === '' || fragmentLog === '' ) {
+
+ haveDiagnostics = false;
+
+ }
+
+ if ( haveDiagnostics ) {
+
+ this.diagnostics = {
+
+ runnable: runnable,
+ material: material,
+
+ programLog: programLog,
+
+ vertexShader: {
+
+ log: vertexLog,
+ prefix: prefixVertex
+
+ },
+
+ fragmentShader: {
+
+ log: fragmentLog,
+ prefix: prefixFragment
+
+ }
+
+ };
+
+ }
+
+ // clean up
+
+ gl.deleteShader( glVertexShader );
+ gl.deleteShader( glFragmentShader );
+
+ // set up caching for uniform locations
+
+ var cachedUniforms;
+
+ this.getUniforms = function() {
+
+ if ( cachedUniforms === undefined ) {
+
+ cachedUniforms = fetchUniformLocations( gl, program );
+
+ }
+
+ return cachedUniforms;
+
+ };
+
+ // set up caching for attribute locations
+
+ var cachedAttributes;
+
+ this.getAttributes = function() {
+
+ if ( cachedAttributes === undefined ) {
+
+ cachedAttributes = fetchAttributeLocations( gl, program );
+
+ }
+
+ return cachedAttributes;
+
+ };
+
+ // free resource
+
+ this.destroy = function() {
+
+ gl.deleteProgram( program );
+ this.program = undefined;
+
+ };
+
+ // DEPRECATED
+
+ Object.defineProperties( this, {
+
+ uniforms: {
+ get: function() {
+
+ console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' );
+ return this.getUniforms();
+
+ }
+ },
+
+ attributes: {
+ get: function() {
+
+ console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' );
+ return this.getAttributes();
+
+ }
+ }
+
+ } );
+
+
+ //
+
+ this.id = programIdCount ++;
+ this.code = code;
+ this.usedTimes = 1;
+ this.program = program;
+ this.vertexShader = glVertexShader;
+ this.fragmentShader = glFragmentShader;
+
+ return this;
+
+ };
+
+} )();
+
+// File:src/renderers/webgl/WebGLPrograms.js
+
+THREE.WebGLPrograms = function ( renderer, capabilities ) {
+
+ var programs = [];
+
+ var shaderIDs = {
+ MeshDepthMaterial: 'depth',
+ MeshNormalMaterial: 'normal',
+ MeshBasicMaterial: 'basic',
+ MeshLambertMaterial: 'lambert',
+ MeshPhongMaterial: 'phong',
+ LineBasicMaterial: 'basic',
+ LineDashedMaterial: 'dashed',
+ PointsMaterial: 'points'
+ };
+
+ var parameterNames = [
+ "precision", "supportsVertexTextures", "map", "envMap", "envMapMode",
+ "lightMap", "aoMap", "emissiveMap", "bumpMap", "normalMap", "displacementMap", "specularMap",
+ "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp",
+ "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
+ "maxBones", "useVertexTexture", "morphTargets", "morphNormals",
+ "maxMorphTargets", "maxMorphNormals", "maxDirLights", "maxPointLights",
+ "maxSpotLights", "maxHemiLights", "maxShadows", "shadowMapEnabled", "pointLightShadows",
+ "shadowMapType", "shadowMapDebug", "alphaTest", "metal", "doubleSided",
+ "flipSided"
+ ];
+
+
+ function allocateBones ( object ) {
+
+ if ( capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture ) {
+
+ return 1024;
+
+ } else {
+
+ // default for when object is not specified
+ // ( for example when prebuilding shader to be used with multiple objects )
+ //
+ // - leave some extra space for other uniforms
+ // - limit here is ANGLE's 254 max uniform vectors
+ // (up to 54 should be safe)
+
+ var nVertexUniforms = capabilities.maxVertexUniforms;
+ var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 );
+
+ var maxBones = nVertexMatrices;
+
+ if ( object !== undefined && object instanceof THREE.SkinnedMesh ) {
+
+ maxBones = Math.min( object.skeleton.bones.length, maxBones );
+
+ if ( maxBones < object.skeleton.bones.length ) {
+
+ console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' );
+
+ }
+
+ }
+
+ return maxBones;
+
+ }
+
+ }
+
+ function allocateLights( lights ) {
+
+ var dirLights = 0;
+ var pointLights = 0;
+ var spotLights = 0;
+ var hemiLights = 0;
+
+ for ( var l = 0, ll = lights.length; l < ll; l ++ ) {
+
+ var light = lights[ l ];
+
+ if ( light.visible === false ) continue;
+
+ if ( light instanceof THREE.DirectionalLight ) dirLights ++;
+ if ( light instanceof THREE.PointLight ) pointLights ++;
+ if ( light instanceof THREE.SpotLight ) spotLights ++;
+ if ( light instanceof THREE.HemisphereLight ) hemiLights ++;
+
+ }
+
+ return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights };
+
+ }
+
+ function allocateShadows( lights ) {
+
+ var maxShadows = 0;
+ var pointLightShadows = 0;
+
+ for ( var l = 0, ll = lights.length; l < ll; l ++ ) {
+
+ var light = lights[ l ];
+
+ if ( ! light.castShadow ) continue;
+
+ if ( light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) maxShadows ++;
+ if ( light instanceof THREE.PointLight ) {
+
+ maxShadows ++;
+ pointLightShadows ++;
+
+ }
+
+ }
+
+ return { 'maxShadows': maxShadows, 'pointLightShadows': pointLightShadows };
+
+ }
+
+ this.getParameters = function ( material, lights, fog, object ) {
+
+ var shaderID = shaderIDs[ material.type ];
+ // heuristics to create shader parameters according to lights in the scene
+ // (not to blow over maxLights budget)
+
+ var maxLightCount = allocateLights( lights );
+ var allocatedShadows = allocateShadows( lights );
+ var maxBones = allocateBones( object );
+ var precision = renderer.getPrecision();
+
+ if ( material.precision !== null ) {
+
+ precision = capabilities.getMaxPrecision( material.precision );
+
+ if ( precision !== material.precision ) {
+
+ console.warn( 'THREE.WebGLRenderer.initMaterial:', material.precision, 'not supported, using', precision, 'instead.' );
+
+ }
+
+ }
+
+ var parameters = {
+
+ shaderID: shaderID,
+
+ precision: precision,
+ supportsVertexTextures: capabilities.vertexTextures,
+
+ map: !! material.map,
+ envMap: !! material.envMap,
+ envMapMode: material.envMap && material.envMap.mapping,
+ lightMap: !! material.lightMap,
+ aoMap: !! material.aoMap,
+ emissiveMap: !! material.emissiveMap,
+ bumpMap: !! material.bumpMap,
+ normalMap: !! material.normalMap,
+ displacementMap: !! material.displacementMap,
+ specularMap: !! material.specularMap,
+ alphaMap: !! material.alphaMap,
+
+ combine: material.combine,
+
+ vertexColors: material.vertexColors,
+
+ fog: fog,
+ useFog: material.fog,
+ fogExp: fog instanceof THREE.FogExp2,
+
+ flatShading: material.shading === THREE.FlatShading,
+
+ sizeAttenuation: material.sizeAttenuation,
+ logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer,
+
+ skinning: material.skinning,
+ maxBones: maxBones,
+ useVertexTexture: capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture,
+
+ morphTargets: material.morphTargets,
+ morphNormals: material.morphNormals,
+ maxMorphTargets: renderer.maxMorphTargets,
+ maxMorphNormals: renderer.maxMorphNormals,
+
+ maxDirLights: maxLightCount.directional,
+ maxPointLights: maxLightCount.point,
+ maxSpotLights: maxLightCount.spot,
+ maxHemiLights: maxLightCount.hemi,
+
+ maxShadows: allocatedShadows.maxShadows,
+ pointLightShadows: allocatedShadows.pointLightShadows,
+ shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && allocatedShadows.maxShadows > 0,
+ shadowMapType: renderer.shadowMap.type,
+ shadowMapDebug: renderer.shadowMap.debug,
+
+ alphaTest: material.alphaTest,
+ metal: material.metal,
+ doubleSided: material.side === THREE.DoubleSide,
+ flipSided: material.side === THREE.BackSide
+
+ };
+
+ return parameters;
+
+ };
+
+ this.getProgramCode = function ( material, parameters ) {
+
+ var chunks = [];
+
+ if ( parameters.shaderID ) {
+
+ chunks.push( parameters.shaderID );
+
+ } else {
+
+ chunks.push( material.fragmentShader );
+ chunks.push( material.vertexShader );
+
+ }
+
+ if ( material.defines !== undefined ) {
+
+ for ( var name in material.defines ) {
+
+ chunks.push( name );
+ chunks.push( material.defines[ name ] );
+
+ }
+
+ }
+
+ for ( var i = 0; i < parameterNames.length; i ++ ) {
+
+ var parameterName = parameterNames[ i ];
+ chunks.push( parameterName );
+ chunks.push( parameters[ parameterName ] );
+
+ }
+
+ return chunks.join();
+
+ };
+
+ this.acquireProgram = function ( material, parameters, code ) {
+
+ var program;
+
+ // Check if code has been already compiled
+ for ( var p = 0, pl = programs.length; p < pl; p ++ ) {
+
+ var programInfo = programs[ p ];
+
+ if ( programInfo.code === code ) {
+
+ program = programInfo;
+ ++ program.usedTimes;
+
+ break;
+
+ }
+
+ }
+
+ if ( program === undefined ) {
+
+ program = new THREE.WebGLProgram( renderer, code, material, parameters );
+ programs.push( program );
+
+ }
+
+ return program;
+
+ };
+
+ this.releaseProgram = function( program ) {
+
+ if ( -- program.usedTimes === 0 ) {
+
+ // Remove from unordered set
+ var i = programs.indexOf( program );
+ programs[ i ] = programs[ programs.length - 1 ];
+ programs.pop();
+
+ // Free WebGL resources
+ program.destroy();
+
+ }
+
+ };
+
+ // Exposed for resource monitoring & error feedback via renderer.info:
+ this.programs = programs;
+
+};
+
+// File:src/renderers/webgl/WebGLProperties.js
+
+/**
+* @author fordacious / fordacious.github.io
+*/
+
+THREE.WebGLProperties = function () {
+
+ var properties = {};
+
+ this.get = function ( object ) {
+
+ var uuid = object.uuid;
+ var map = properties[ uuid ];
+
+ if ( map === undefined ) {
+
+ map = {};
+ properties[ uuid ] = map;
+
+ }
+
+ return map;
+
+ };
+
+ this.delete = function ( object ) {
+
+ delete properties[ object.uuid ];
+
+ };
+
+ this.clear = function () {
+
+ properties = {};
+
+ };
+
+};
+
+// File:src/renderers/webgl/WebGLShader.js
+
+THREE.WebGLShader = ( function () {
+
+ function addLineNumbers( string ) {
+
+ var lines = string.split( '\n' );
+
+ for ( var i = 0; i < lines.length; i ++ ) {
+
+ lines[ i ] = ( i + 1 ) + ': ' + lines[ i ];
+
+ }
+
+ return lines.join( '\n' );
+
+ }
+
+ return function WebGLShader( gl, type, string ) {
+
+ var shader = gl.createShader( type );
+
+ gl.shaderSource( shader, string );
+ gl.compileShader( shader );
+
+ if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) {
+
+ console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' );
+
+ }
+
+ if ( gl.getShaderInfoLog( shader ) !== '' ) {
+
+ console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) );
+
+ }
+
+ // --enable-privileged-webgl-extension
+ // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) );
+
+ return shader;
+
+ };
+
+} )();
+
+// File:src/renderers/webgl/WebGLShadowMap.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
+
+ var _gl = _renderer.context,
+ _state = _renderer.state,
+ _frustum = new THREE.Frustum(),
+ _projScreenMatrix = new THREE.Matrix4(),
+
+ _min = new THREE.Vector3(),
+ _max = new THREE.Vector3(),
+
+ _lookTarget = new THREE.Vector3(),
+ _lightPositionWorld = new THREE.Vector3(),
+
+ _renderList = [],
+
+ _MorphingFlag = 1,
+ _SkinningFlag = 2,
+
+ _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1,
+
+ _depthMaterials = new Array( _NumberOfMaterialVariants ),
+ _distanceMaterials = new Array( _NumberOfMaterialVariants );
+
+ var cubeDirections = [
+ new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( - 1, 0, 0 ), new THREE.Vector3( 0, 0, 1 ),
+ new THREE.Vector3( 0, 0, - 1 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, - 1, 0 )
+ ];
+
+ var cubeUps = [
+ new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ),
+ new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ), new THREE.Vector3( 0, 0, - 1 )
+ ];
+
+ var cube2DViewPorts = [
+ new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(),
+ new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4()
+ ];
+
+ var _vector4 = new THREE.Vector4();
+
+ // init
+
+ var depthShader = THREE.ShaderLib[ "depthRGBA" ];
+ var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
+
+ var distanceShader = THREE.ShaderLib[ "distanceRGBA" ];
+ var distanceUniforms = THREE.UniformsUtils.clone( distanceShader.uniforms );
+
+ for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) {
+
+ var useMorphing = ( i & _MorphingFlag ) !== 0;
+ var useSkinning = ( i & _SkinningFlag ) !== 0;
+
+ var depthMaterial = new THREE.ShaderMaterial( {
+ uniforms: depthUniforms,
+ vertexShader: depthShader.vertexShader,
+ fragmentShader: depthShader.fragmentShader,
+ morphTargets: useMorphing,
+ skinning: useSkinning
+ } );
+
+ depthMaterial._shadowPass = true;
+
+ _depthMaterials[ i ] = depthMaterial;
+
+ var distanceMaterial = new THREE.ShaderMaterial( {
+ uniforms: distanceUniforms,
+ vertexShader: distanceShader.vertexShader,
+ fragmentShader: distanceShader.fragmentShader,
+ morphTargets: useMorphing,
+ skinning: useSkinning
+ } );
+
+ distanceMaterial._shadowPass = true;
+
+ _distanceMaterials[ i ] = distanceMaterial;
+
+ }
+
+ //
+
+ var scope = this;
+
+ this.enabled = false;
+
+ this.autoUpdate = true;
+ this.needsUpdate = false;
+
+ this.type = THREE.PCFShadowMap;
+ this.cullFace = THREE.CullFaceFront;
+
+ this.render = function ( scene ) {
+
+ var faceCount, isPointLight;
+
+ if ( scope.enabled === false ) return;
+ if ( scope.autoUpdate === false && scope.needsUpdate === false ) return;
+
+ // Set GL state for depth map.
+ _gl.clearColor( 1, 1, 1, 1 );
+ _state.disable( _gl.BLEND );
+ _state.enable( _gl.CULL_FACE );
+ _gl.frontFace( _gl.CCW );
+ _gl.cullFace( scope.cullFace === THREE.CullFaceFront ? _gl.FRONT : _gl.BACK );
+ _state.setDepthTest( true );
+
+ // save the existing viewport so it can be restored later
+ _renderer.getViewport( _vector4 );
+
+ // render depth map
+
+ for ( var i = 0, il = _lights.length; i < il; i ++ ) {
+
+ var light = _lights[ i ];
+
+ if ( light.castShadow === true ) {
+
+ var shadow = light.shadow;
+ var shadowCamera = shadow.camera;
+ var shadowMapSize = shadow.mapSize;
+
+ if ( light instanceof THREE.PointLight ) {
+
+ faceCount = 6;
+ isPointLight = true;
+
+ var vpWidth = shadowMapSize.x / 4.0;
+ var vpHeight = shadowMapSize.y / 2.0;
+
+ // These viewports map a cube-map onto a 2D texture with the
+ // following orientation:
+ //
+ // xzXZ
+ // y Y
+ //
+ // X - Positive x direction
+ // x - Negative x direction
+ // Y - Positive y direction
+ // y - Negative y direction
+ // Z - Positive z direction
+ // z - Negative z direction
+
+ // positive X
+ cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight );
+ // negative X
+ cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight );
+ // positive Z
+ cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight );
+ // negative Z
+ cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight );
+ // positive Y
+ cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight );
+ // negative Y
+ cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight );
+
+ } else {
+
+ faceCount = 1;
+ isPointLight = false;
+
+ }
+
+ if ( shadow.map === null ) {
+
+ var shadowFilter = THREE.LinearFilter;
+
+ if ( scope.type === THREE.PCFSoftShadowMap ) {
+
+ shadowFilter = THREE.NearestFilter;
+
+ }
+
+ var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat };
+
+ shadow.map = new THREE.WebGLRenderTarget( shadowMapSize.x, shadowMapSize.y, pars );
+ shadow.matrix = new THREE.Matrix4();
+
+ //
+
+ if ( light instanceof THREE.SpotLight ) {
+
+ shadowCamera.aspect = shadowMapSize.x / shadowMapSize.y;
+
+ }
+
+ shadowCamera.updateProjectionMatrix();
+
+ }
+
+ var shadowMap = shadow.map;
+ var shadowMatrix = shadow.matrix;
+
+ _lightPositionWorld.setFromMatrixPosition( light.matrixWorld );
+ shadowCamera.position.copy( _lightPositionWorld );
+
+ _renderer.setRenderTarget( shadowMap );
+ _renderer.clear();
+
+ // render shadow map for each cube face (if omni-directional) or
+ // run a single pass if not
+
+ for ( var face = 0; face < faceCount; face ++ ) {
+
+ if ( isPointLight ) {
+
+ _lookTarget.copy( shadowCamera.position );
+ _lookTarget.add( cubeDirections[ face ] );
+ shadowCamera.up.copy( cubeUps[ face ] );
+ shadowCamera.lookAt( _lookTarget );
+ var vpDimensions = cube2DViewPorts[ face ];
+ _renderer.setViewport( vpDimensions.x, vpDimensions.y, vpDimensions.z, vpDimensions.w );
+
+ } else {
+
+ _lookTarget.setFromMatrixPosition( light.target.matrixWorld );
+ shadowCamera.lookAt( _lookTarget );
+
+ }
+
+ shadowCamera.updateMatrixWorld();
+ shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
+
+ // compute shadow matrix
+
+ shadowMatrix.set(
+ 0.5, 0.0, 0.0, 0.5,
+ 0.0, 0.5, 0.0, 0.5,
+ 0.0, 0.0, 0.5, 0.5,
+ 0.0, 0.0, 0.0, 1.0
+ );
+
+ shadowMatrix.multiply( shadowCamera.projectionMatrix );
+ shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
+
+ // update camera matrices and frustum
+
+ _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
+ _frustum.setFromMatrix( _projScreenMatrix );
+
+ // set object matrices & frustum culling
+
+ _renderList.length = 0;
+
+ projectObject( scene, shadowCamera );
+
+ // render shadow map
+ // render regular objects
+
+ for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) {
+
+ var object = _renderList[ j ];
+ var geometry = _objects.update( object );
+ var material = object.material;
+
+ if ( material instanceof THREE.MeshFaceMaterial ) {
+
+ var groups = geometry.groups;
+ var materials = material.materials;
+
+ for ( var k = 0, kl = groups.length; k < kl; k ++ ) {
+
+ var group = groups[ k ];
+ var groupMaterial = materials[ group.materialIndex ];
+
+ if ( groupMaterial.visible === true ) {
+
+ var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld );
+ _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, group );
+
+ }
+
+ }
+
+ } else {
+
+ var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld );
+ _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, null );
+
+ }
+
+ }
+
+ }
+
+ // We must call _renderer.resetGLState() at the end of each iteration of
+ // the light loop in order to force material updates for each light.
+ _renderer.resetGLState();
+
+ }
+
+ }
+
+ _renderer.setViewport( _vector4.x, _vector4.y, _vector4.z, _vector4.w );
+
+ // Restore GL state.
+ var clearColor = _renderer.getClearColor(),
+ clearAlpha = _renderer.getClearAlpha();
+ _renderer.setClearColor( clearColor, clearAlpha );
+ _state.enable( _gl.BLEND );
+
+ if ( scope.cullFace === THREE.CullFaceFront ) {
+
+ _gl.cullFace( _gl.BACK );
+
+ }
+
+ _renderer.resetGLState();
+
+ scope.needsUpdate = false;
+
+ };
+
+ function getDepthMaterial( object, material, isPointLight, lightPositionWorld ) {
+
+ var geometry = object.geometry;
+
+ var newMaterial = null;
+
+ var materialVariants = _depthMaterials;
+ var customMaterial = object.customDepthMaterial;
+
+ if ( isPointLight ) {
+
+ materialVariants = _distanceMaterials;
+ customMaterial = object.customDistanceMaterial;
+
+ }
+
+ if ( ! customMaterial ) {
+
+ var useMorphing = geometry.morphTargets !== undefined &&
+ geometry.morphTargets.length > 0 && material.morphTargets;
+
+ var useSkinning = object instanceof THREE.SkinnedMesh && material.skinning;
+
+ var variantIndex = 0;
+
+ if ( useMorphing ) variantIndex |= _MorphingFlag;
+ if ( useSkinning ) variantIndex |= _SkinningFlag;
+
+ newMaterial = materialVariants[ variantIndex ];
+
+ } else {
+
+ newMaterial = customMaterial;
+
+ }
+
+ newMaterial.visible = material.visible;
+ newMaterial.wireframe = material.wireframe;
+ newMaterial.wireframeLinewidth = material.wireframeLinewidth;
+
+ if ( isPointLight && newMaterial.uniforms.lightPos !== undefined ) {
+
+ newMaterial.uniforms.lightPos.value.copy( lightPositionWorld );
+
+ }
+
+ return newMaterial;
+
+ }
+
+ function projectObject( object, camera ) {
+
+ if ( object.visible === false ) return;
+
+ if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) {
+
+ if ( object.castShadow && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) {
+
+ var material = object.material;
+
+ if ( material.visible === true ) {
+
+ object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+ _renderList.push( object );
+
+ }
+
+ }
+
+ }
+
+ var children = object.children;
+
+ for ( var i = 0, l = children.length; i < l; i ++ ) {
+
+ projectObject( children[ i ], camera );
+
+ }
+
+ }
+
+};
+
+// File:src/renderers/webgl/WebGLState.js
+
+/**
+* @author mrdoob / http://mrdoob.com/
+*/
+
+THREE.WebGLState = function ( gl, extensions, paramThreeToGL ) {
+
+ var _this = this;
+
+ var newAttributes = new Uint8Array( 16 );
+ var enabledAttributes = new Uint8Array( 16 );
+ var attributeDivisors = new Uint8Array( 16 );
+
+ var capabilities = {};
+
+ var compressedTextureFormats = null;
+
+ var currentBlending = null;
+ var currentBlendEquation = null;
+ var currentBlendSrc = null;
+ var currentBlendDst = null;
+ var currentBlendEquationAlpha = null;
+ var currentBlendSrcAlpha = null;
+ var currentBlendDstAlpha = null;
+
+ var currentDepthFunc = null;
+ var currentDepthWrite = null;
+
+ var currentColorWrite = null;
+
+ var currentFlipSided = null;
+
+ var currentLineWidth = null;
+
+ var currentPolygonOffsetFactor = null;
+ var currentPolygonOffsetUnits = null;
+
+ var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );
+
+ var currentTextureSlot = undefined;
+ var currentBoundTextures = {};
+
+ this.init = function () {
+
+ gl.clearColor( 0, 0, 0, 1 );
+ gl.clearDepth( 1 );
+ gl.clearStencil( 0 );
+
+ this.enable( gl.DEPTH_TEST );
+ gl.depthFunc( gl.LEQUAL );
+
+ gl.frontFace( gl.CCW );
+ gl.cullFace( gl.BACK );
+ this.enable( gl.CULL_FACE );
+
+ this.enable( gl.BLEND );
+ gl.blendEquation( gl.FUNC_ADD );
+ gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );
+
+ };
+
+ this.initAttributes = function () {
+
+ for ( var i = 0, l = newAttributes.length; i < l; i ++ ) {
+
+ newAttributes[ i ] = 0;
+
+ }
+
+ };
+
+ this.enableAttribute = function ( attribute ) {
+
+ newAttributes[ attribute ] = 1;
+
+ if ( enabledAttributes[ attribute ] === 0 ) {
+
+ gl.enableVertexAttribArray( attribute );
+ enabledAttributes[ attribute ] = 1;
+
+ }
+
+ if ( attributeDivisors[ attribute ] !== 0 ) {
+
+ var extension = extensions.get( 'ANGLE_instanced_arrays' );
+
+ extension.vertexAttribDivisorANGLE( attribute, 0 );
+ attributeDivisors[ attribute ] = 0;
+
+ }
+
+ };
+
+ this.enableAttributeAndDivisor = function ( attribute, meshPerAttribute, extension ) {
+
+ newAttributes[ attribute ] = 1;
+
+ if ( enabledAttributes[ attribute ] === 0 ) {
+
+ gl.enableVertexAttribArray( attribute );
+ enabledAttributes[ attribute ] = 1;
+
+ }
+
+ if ( attributeDivisors[ attribute ] !== meshPerAttribute ) {
+
+ extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute );
+ attributeDivisors[ attribute ] = meshPerAttribute;
+
+ }
+
+ };
+
+ this.disableUnusedAttributes = function () {
+
+ for ( var i = 0, l = enabledAttributes.length; i < l; i ++ ) {
+
+ if ( enabledAttributes[ i ] !== newAttributes[ i ] ) {
+
+ gl.disableVertexAttribArray( i );
+ enabledAttributes[ i ] = 0;
+
+ }
+
+ }
+
+ };
+
+ this.enable = function ( id ) {
+
+ if ( capabilities[ id ] !== true ) {
+
+ gl.enable( id );
+ capabilities[ id ] = true;
+
+ }
+
+ };
+
+ this.disable = function ( id ) {
+
+ if ( capabilities[ id ] !== false ) {
+
+ gl.disable( id );
+ capabilities[ id ] = false;
+
+ }
+
+ };
+
+ this.getCompressedTextureFormats = function () {
+
+ if ( compressedTextureFormats === null ) {
+
+ compressedTextureFormats = [];
+
+ if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) ||
+ extensions.get( 'WEBGL_compressed_texture_s3tc' ) ) {
+
+ var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS );
+
+ for ( var i = 0; i < formats.length; i ++ ) {
+
+ compressedTextureFormats.push( formats[ i ] );
+
+ }
+
+ }
+
+ }
+
+ return compressedTextureFormats;
+
+ };
+
+ this.setBlending = function ( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha ) {
+
+ if ( blending !== currentBlending ) {
+
+ if ( blending === THREE.NoBlending ) {
+
+ this.disable( gl.BLEND );
+
+ } else if ( blending === THREE.AdditiveBlending ) {
+
+ this.enable( gl.BLEND );
+ gl.blendEquation( gl.FUNC_ADD );
+ gl.blendFunc( gl.SRC_ALPHA, gl.ONE );
+
+ } else if ( blending === THREE.SubtractiveBlending ) {
+
+ // TODO: Find blendFuncSeparate() combination
+
+ this.enable( gl.BLEND );
+ gl.blendEquation( gl.FUNC_ADD );
+ gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR );
+
+ } else if ( blending === THREE.MultiplyBlending ) {
+
+ // TODO: Find blendFuncSeparate() combination
+
+ this.enable( gl.BLEND );
+ gl.blendEquation( gl.FUNC_ADD );
+ gl.blendFunc( gl.ZERO, gl.SRC_COLOR );
+
+ } else if ( blending === THREE.CustomBlending ) {
+
+ this.enable( gl.BLEND );
+
+ } else {
+
+ this.enable( gl.BLEND );
+ gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
+ gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
+
+ }
+
+ currentBlending = blending;
+
+ }
+
+ if ( blending === THREE.CustomBlending ) {
+
+ blendEquationAlpha = blendEquationAlpha || blendEquation;
+ blendSrcAlpha = blendSrcAlpha || blendSrc;
+ blendDstAlpha = blendDstAlpha || blendDst;
+
+ if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {
+
+ gl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) );
+
+ currentBlendEquation = blendEquation;
+ currentBlendEquationAlpha = blendEquationAlpha;
+
+ }
+
+ if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {
+
+ gl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) );
+
+ currentBlendSrc = blendSrc;
+ currentBlendDst = blendDst;
+ currentBlendSrcAlpha = blendSrcAlpha;
+ currentBlendDstAlpha = blendDstAlpha;
+
+ }
+
+ } else {
+
+ currentBlendEquation = null;
+ currentBlendSrc = null;
+ currentBlendDst = null;
+ currentBlendEquationAlpha = null;
+ currentBlendSrcAlpha = null;
+ currentBlendDstAlpha = null;
+
+ }
+
+ };
+
+ this.setDepthFunc = function ( depthFunc ) {
+
+ if ( currentDepthFunc !== depthFunc ) {
+
+ if ( depthFunc ) {
+
+ switch ( depthFunc ) {
+
+ case THREE.NeverDepth:
+
+ gl.depthFunc( gl.NEVER );
+ break;
+
+ case THREE.AlwaysDepth:
+
+ gl.depthFunc( gl.ALWAYS );
+ break;
+
+ case THREE.LessDepth:
+
+ gl.depthFunc( gl.LESS );
+ break;
+
+ case THREE.LessEqualDepth:
+
+ gl.depthFunc( gl.LEQUAL );
+ break;
+
+ case THREE.EqualDepth:
+
+ gl.depthFunc( gl.EQUAL );
+ break;
+
+ case THREE.GreaterEqualDepth:
+
+ gl.depthFunc( gl.GEQUAL );
+ break;
+
+ case THREE.GreaterDepth:
+
+ gl.depthFunc( gl.GREATER );
+ break;
+
+ case THREE.NotEqualDepth:
+
+ gl.depthFunc( gl.NOTEQUAL );
+ break;
+
+ default:
+
+ gl.depthFunc( gl.LEQUAL );
+
+ }
+
+ } else {
+
+ gl.depthFunc( gl.LEQUAL );
+
+ }
+
+ currentDepthFunc = depthFunc;
+
+ }
+
+ };
+
+ this.setDepthTest = function ( depthTest ) {
+
+ if ( depthTest ) {
+
+ this.enable( gl.DEPTH_TEST );
+
+ } else {
+
+ this.disable( gl.DEPTH_TEST );
+
+ }
+
+ };
+
+ this.setDepthWrite = function ( depthWrite ) {
+
+ if ( currentDepthWrite !== depthWrite ) {
+
+ gl.depthMask( depthWrite );
+ currentDepthWrite = depthWrite;
+
+ }
+
+ };
+
+ this.setColorWrite = function ( colorWrite ) {
+
+ if ( currentColorWrite !== colorWrite ) {
+
+ gl.colorMask( colorWrite, colorWrite, colorWrite, colorWrite );
+ currentColorWrite = colorWrite;
+
+ }
+
+ };
+
+ this.setFlipSided = function ( flipSided ) {
+
+ if ( currentFlipSided !== flipSided ) {
+
+ if ( flipSided ) {
+
+ gl.frontFace( gl.CW );
+
+ } else {
+
+ gl.frontFace( gl.CCW );
+
+ }
+
+ currentFlipSided = flipSided;
+
+ }
+
+ };
+
+ this.setLineWidth = function ( width ) {
+
+ if ( width !== currentLineWidth ) {
+
+ gl.lineWidth( width );
+
+ currentLineWidth = width;
+
+ }
+
+ };
+
+ this.setPolygonOffset = function ( polygonOffset, factor, units ) {
+
+ if ( polygonOffset ) {
+
+ this.enable( gl.POLYGON_OFFSET_FILL );
+
+ } else {
+
+ this.disable( gl.POLYGON_OFFSET_FILL );
+
+ }
+
+ if ( polygonOffset && ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) ) {
+
+ gl.polygonOffset( factor, units );
+
+ currentPolygonOffsetFactor = factor;
+ currentPolygonOffsetUnits = units;
+
+ }
+
+ };
+
+ this.setScissorTest = function ( scissorTest ) {
+
+ if ( scissorTest ) {
+
+ this.enable( gl.SCISSOR_TEST );
+
+ } else {
+
+ this.disable( gl.SCISSOR_TEST );
+
+ }
+
+ };
+
+ // texture
+
+ this.activeTexture = function ( webglSlot ) {
+
+ if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;
+
+ if ( currentTextureSlot !== webglSlot ) {
+
+ gl.activeTexture( webglSlot );
+ currentTextureSlot = webglSlot;
+
+ }
+
+ }
+
+ this.bindTexture = function ( webglType, webglTexture ) {
+
+ if ( currentTextureSlot === undefined ) {
+
+ _this.activeTexture();
+
+ }
+
+ var boundTexture = currentBoundTextures[ currentTextureSlot ];
+
+ if ( boundTexture === undefined ) {
+
+ boundTexture = { type: undefined, texture: undefined };
+ currentBoundTextures[ currentTextureSlot ] = boundTexture;
+
+ }
+
+ if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {
+
+ gl.bindTexture( webglType, webglTexture );
+
+ boundTexture.type = webglType;
+ boundTexture.texture = webglTexture;
+
+ }
+
+ };
+
+ this.compressedTexImage2D = function () {
+
+ try {
+
+ gl.compressedTexImage2D.apply( gl, arguments );
+
+ } catch ( error ) {
+
+ console.error( error );
+
+ }
+
+ };
+
+ this.texImage2D = function () {
+
+ try {
+
+ gl.texImage2D.apply( gl, arguments );
+
+ } catch ( error ) {
+
+ console.error( error );
+
+ }
+
+ };
+
+ //
+
+ this.reset = function () {
+
+ for ( var i = 0; i < enabledAttributes.length; i ++ ) {
+
+ if ( enabledAttributes[ i ] === 1 ) {
+
+ gl.disableVertexAttribArray( i );
+ enabledAttributes[ i ] = 0;
+
+ }
+
+ }
+
+ capabilities = {};
+
+ compressedTextureFormats = null;
+
+ currentBlending = null;
+
+ currentDepthWrite = null;
+ currentColorWrite = null;
+
+ currentFlipSided = null;
+
+ };
+
+};
+
+// File:src/renderers/webgl/plugins/LensFlarePlugin.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.LensFlarePlugin = function ( renderer, flares ) {
+
+ var gl = renderer.context;
+ var state = renderer.state;
+
+ var vertexBuffer, elementBuffer;
+ var program, attributes, uniforms;
+ var hasVertexTexture;
+
+ var tempTexture, occlusionTexture;
+
+ function init() {
+
+ var vertices = new Float32Array( [
+ - 1, - 1, 0, 0,
+ 1, - 1, 1, 0,
+ 1, 1, 1, 1,
+ - 1, 1, 0, 1
+ ] );
+
+ var faces = new Uint16Array( [
+ 0, 1, 2,
+ 0, 2, 3
+ ] );
+
+ // buffers
+
+ vertexBuffer = gl.createBuffer();
+ elementBuffer = gl.createBuffer();
+
+ gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
+ gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
+
+ gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
+ gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
+
+ // textures
+
+ tempTexture = gl.createTexture();
+ occlusionTexture = gl.createTexture();
+
+ state.bindTexture( gl.TEXTURE_2D, tempTexture );
+ gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
+
+ state.bindTexture( gl.TEXTURE_2D, occlusionTexture );
+ gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
+
+ hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0;
+
+ var shader;
+
+ if ( hasVertexTexture ) {
+
+ shader = {
+
+ vertexShader: [
+
+ "uniform lowp int renderType;",
+
+ "uniform vec3 screenPosition;",
+ "uniform vec2 scale;",
+ "uniform float rotation;",
+
+ "uniform sampler2D occlusionMap;",
+
+ "attribute vec2 position;",
+ "attribute vec2 uv;",
+
+ "varying vec2 vUV;",
+ "varying float vVisibility;",
+
+ "void main() {",
+
+ "vUV = uv;",
+
+ "vec2 pos = position;",
+
+ "if ( renderType == 2 ) {",
+
+ "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );",
+ "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );",
+
+ "vVisibility = visibility.r / 9.0;",
+ "vVisibility *= 1.0 - visibility.g / 9.0;",
+ "vVisibility *= visibility.b / 9.0;",
+ "vVisibility *= 1.0 - visibility.a / 9.0;",
+
+ "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
+ "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
+
+ "}",
+
+ "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "uniform lowp int renderType;",
+
+ "uniform sampler2D map;",
+ "uniform float opacity;",
+ "uniform vec3 color;",
+
+ "varying vec2 vUV;",
+ "varying float vVisibility;",
+
+ "void main() {",
+
+ // pink square
+
+ "if ( renderType == 0 ) {",
+
+ "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
+
+ // restore
+
+ "} else if ( renderType == 1 ) {",
+
+ "gl_FragColor = texture2D( map, vUV );",
+
+ // flare
+
+ "} else {",
+
+ "vec4 texture = texture2D( map, vUV );",
+ "texture.a *= opacity * vVisibility;",
+ "gl_FragColor = texture;",
+ "gl_FragColor.rgb *= color;",
+
+ "}",
+
+ "}"
+
+ ].join( "\n" )
+
+ };
+
+ } else {
+
+ shader = {
+
+ vertexShader: [
+
+ "uniform lowp int renderType;",
+
+ "uniform vec3 screenPosition;",
+ "uniform vec2 scale;",
+ "uniform float rotation;",
+
+ "attribute vec2 position;",
+ "attribute vec2 uv;",
+
+ "varying vec2 vUV;",
+
+ "void main() {",
+
+ "vUV = uv;",
+
+ "vec2 pos = position;",
+
+ "if ( renderType == 2 ) {",
+
+ "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
+ "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
+
+ "}",
+
+ "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
+
+ "}"
+
+ ].join( "\n" ),
+
+ fragmentShader: [
+
+ "precision mediump float;",
+
+ "uniform lowp int renderType;",
+
+ "uniform sampler2D map;",
+ "uniform sampler2D occlusionMap;",
+ "uniform float opacity;",
+ "uniform vec3 color;",
+
+ "varying vec2 vUV;",
+
+ "void main() {",
+
+ // pink square
+
+ "if ( renderType == 0 ) {",
+
+ "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );",
+
+ // restore
+
+ "} else if ( renderType == 1 ) {",
+
+ "gl_FragColor = texture2D( map, vUV );",
+
+ // flare
+
+ "} else {",
+
+ "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;",
+ "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;",
+ "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;",
+ "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;",
+ "visibility = ( 1.0 - visibility / 4.0 );",
+
+ "vec4 texture = texture2D( map, vUV );",
+ "texture.a *= opacity * visibility;",
+ "gl_FragColor = texture;",
+ "gl_FragColor.rgb *= color;",
+
+ "}",
+
+ "}"
+
+ ].join( "\n" )
+
+ };
+
+ }
+
+ program = createProgram( shader );
+
+ attributes = {
+ vertex: gl.getAttribLocation ( program, "position" ),
+ uv: gl.getAttribLocation ( program, "uv" )
+ };
+
+ uniforms = {
+ renderType: gl.getUniformLocation( program, "renderType" ),
+ map: gl.getUniformLocation( program, "map" ),
+ occlusionMap: gl.getUniformLocation( program, "occlusionMap" ),
+ opacity: gl.getUniformLocation( program, "opacity" ),
+ color: gl.getUniformLocation( program, "color" ),
+ scale: gl.getUniformLocation( program, "scale" ),
+ rotation: gl.getUniformLocation( program, "rotation" ),
+ screenPosition: gl.getUniformLocation( program, "screenPosition" )
+ };
+
+ }
+
+ /*
+ * Render lens flares
+ * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
+ * reads these back and calculates occlusion.
+ */
+
+ this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
+
+ if ( flares.length === 0 ) return;
+
+ var tempPosition = new THREE.Vector3();
+
+ var invAspect = viewportHeight / viewportWidth,
+ halfViewportWidth = viewportWidth * 0.5,
+ halfViewportHeight = viewportHeight * 0.5;
+
+ var size = 16 / viewportHeight,
+ scale = new THREE.Vector2( size * invAspect, size );
+
+ var screenPosition = new THREE.Vector3( 1, 1, 0 ),
+ screenPositionPixels = new THREE.Vector2( 1, 1 );
+
+ if ( program === undefined ) {
+
+ init();
+
+ }
+
+ gl.useProgram( program );
+
+ state.initAttributes();
+ state.enableAttribute( attributes.vertex );
+ state.enableAttribute( attributes.uv );
+ state.disableUnusedAttributes();
+
+ // loop through all lens flares to update their occlusion and positions
+ // setup gl and common used attribs/uniforms
+
+ gl.uniform1i( uniforms.occlusionMap, 0 );
+ gl.uniform1i( uniforms.map, 1 );
+
+ gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
+ gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 );
+ gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );
+
+ gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
+
+ state.disable( gl.CULL_FACE );
+ gl.depthMask( false );
+
+ for ( var i = 0, l = flares.length; i < l; i ++ ) {
+
+ size = 16 / viewportHeight;
+ scale.set( size * invAspect, size );
+
+ // calc object screen position
+
+ var flare = flares[ i ];
+
+ tempPosition.set( flare.matrixWorld.elements[ 12 ], flare.matrixWorld.elements[ 13 ], flare.matrixWorld.elements[ 14 ] );
+
+ tempPosition.applyMatrix4( camera.matrixWorldInverse );
+ tempPosition.applyProjection( camera.projectionMatrix );
+
+ // setup arrays for gl programs
+
+ screenPosition.copy( tempPosition );
+
+ screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth;
+ screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight;
+
+ // screen cull
+
+ if ( hasVertexTexture || (
+ screenPositionPixels.x > 0 &&
+ screenPositionPixels.x < viewportWidth &&
+ screenPositionPixels.y > 0 &&
+ screenPositionPixels.y < viewportHeight ) ) {
+
+ // save current RGB to temp texture
+
+ state.activeTexture( gl.TEXTURE0 );
+ state.bindTexture( gl.TEXTURE_2D, null );
+ state.activeTexture( gl.TEXTURE1 );
+ state.bindTexture( gl.TEXTURE_2D, tempTexture );
+ gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
+
+
+ // render pink quad
+
+ gl.uniform1i( uniforms.renderType, 0 );
+ gl.uniform2f( uniforms.scale, scale.x, scale.y );
+ gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
+
+ state.disable( gl.BLEND );
+ state.enable( gl.DEPTH_TEST );
+
+ gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
+
+
+ // copy result to occlusionMap
+
+ state.activeTexture( gl.TEXTURE0 );
+ state.bindTexture( gl.TEXTURE_2D, occlusionTexture );
+ gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
+
+
+ // restore graphics
+
+ gl.uniform1i( uniforms.renderType, 1 );
+ state.disable( gl.DEPTH_TEST );
+
+ state.activeTexture( gl.TEXTURE1 );
+ state.bindTexture( gl.TEXTURE_2D, tempTexture );
+ gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
+
+
+ // update object positions
+
+ flare.positionScreen.copy( screenPosition );
+
+ if ( flare.customUpdateCallback ) {
+
+ flare.customUpdateCallback( flare );
+
+ } else {
+
+ flare.updateLensFlares();
+
+ }
+
+ // render flares
+
+ gl.uniform1i( uniforms.renderType, 2 );
+ state.enable( gl.BLEND );
+
+ for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) {
+
+ var sprite = flare.lensFlares[ j ];
+
+ if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) {
+
+ screenPosition.x = sprite.x;
+ screenPosition.y = sprite.y;
+ screenPosition.z = sprite.z;
+
+ size = sprite.size * sprite.scale / viewportHeight;
+
+ scale.x = size * invAspect;
+ scale.y = size;
+
+ gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
+ gl.uniform2f( uniforms.scale, scale.x, scale.y );
+ gl.uniform1f( uniforms.rotation, sprite.rotation );
+
+ gl.uniform1f( uniforms.opacity, sprite.opacity );
+ gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b );
+
+ state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst );
+ renderer.setTexture( sprite.texture, 1 );
+
+ gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ // restore gl
+
+ state.enable( gl.CULL_FACE );
+ state.enable( gl.DEPTH_TEST );
+ gl.depthMask( true );
+
+ renderer.resetGLState();
+
+ };
+
+ function createProgram ( shader ) {
+
+ var program = gl.createProgram();
+
+ var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
+ var vertexShader = gl.createShader( gl.VERTEX_SHADER );
+
+ var prefix = "precision " + renderer.getPrecision() + " float;\n";
+
+ gl.shaderSource( fragmentShader, prefix + shader.fragmentShader );
+ gl.shaderSource( vertexShader, prefix + shader.vertexShader );
+
+ gl.compileShader( fragmentShader );
+ gl.compileShader( vertexShader );
+
+ gl.attachShader( program, fragmentShader );
+ gl.attachShader( program, vertexShader );
+
+ gl.linkProgram( program );
+
+ return program;
+
+ }
+
+};
+
+// File:src/renderers/webgl/plugins/SpritePlugin.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SpritePlugin = function ( renderer, sprites ) {
+
+ var gl = renderer.context;
+ var state = renderer.state;
+
+ var vertexBuffer, elementBuffer;
+ var program, attributes, uniforms;
+
+ var texture;
+
+ // decompose matrixWorld
+
+ var spritePosition = new THREE.Vector3();
+ var spriteRotation = new THREE.Quaternion();
+ var spriteScale = new THREE.Vector3();
+
+ function init() {
+
+ var vertices = new Float32Array( [
+ - 0.5, - 0.5, 0, 0,
+ 0.5, - 0.5, 1, 0,
+ 0.5, 0.5, 1, 1,
+ - 0.5, 0.5, 0, 1
+ ] );
+
+ var faces = new Uint16Array( [
+ 0, 1, 2,
+ 0, 2, 3
+ ] );
+
+ vertexBuffer = gl.createBuffer();
+ elementBuffer = gl.createBuffer();
+
+ gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
+ gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
+
+ gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
+ gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
+
+ program = createProgram();
+
+ attributes = {
+ position: gl.getAttribLocation ( program, 'position' ),
+ uv: gl.getAttribLocation ( program, 'uv' )
+ };
+
+ uniforms = {
+ uvOffset: gl.getUniformLocation( program, 'uvOffset' ),
+ uvScale: gl.getUniformLocation( program, 'uvScale' ),
+
+ rotation: gl.getUniformLocation( program, 'rotation' ),
+ scale: gl.getUniformLocation( program, 'scale' ),
+
+ color: gl.getUniformLocation( program, 'color' ),
+ map: gl.getUniformLocation( program, 'map' ),
+ opacity: gl.getUniformLocation( program, 'opacity' ),
+
+ modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ),
+ projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ),
+
+ fogType: gl.getUniformLocation( program, 'fogType' ),
+ fogDensity: gl.getUniformLocation( program, 'fogDensity' ),
+ fogNear: gl.getUniformLocation( program, 'fogNear' ),
+ fogFar: gl.getUniformLocation( program, 'fogFar' ),
+ fogColor: gl.getUniformLocation( program, 'fogColor' ),
+
+ alphaTest: gl.getUniformLocation( program, 'alphaTest' )
+ };
+
+ var canvas = document.createElement( 'canvas' );
+ canvas.width = 8;
+ canvas.height = 8;
+
+ var context = canvas.getContext( '2d' );
+ context.fillStyle = 'white';
+ context.fillRect( 0, 0, 8, 8 );
+
+ texture = new THREE.Texture( canvas );
+ texture.needsUpdate = true;
+
+ }
+
+ this.render = function ( scene, camera ) {
+
+ if ( sprites.length === 0 ) return;
+
+ // setup gl
+
+ if ( program === undefined ) {
+
+ init();
+
+ }
+
+ gl.useProgram( program );
+
+ state.initAttributes();
+ state.enableAttribute( attributes.position );
+ state.enableAttribute( attributes.uv );
+ state.disableUnusedAttributes();
+
+ state.disable( gl.CULL_FACE );
+ state.enable( gl.BLEND );
+
+ gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
+ gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 );
+ gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );
+
+ gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
+
+ gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
+
+ state.activeTexture( gl.TEXTURE0 );
+ gl.uniform1i( uniforms.map, 0 );
+
+ var oldFogType = 0;
+ var sceneFogType = 0;
+ var fog = scene.fog;
+
+ if ( fog ) {
+
+ gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b );
+
+ if ( fog instanceof THREE.Fog ) {
+
+ gl.uniform1f( uniforms.fogNear, fog.near );
+ gl.uniform1f( uniforms.fogFar, fog.far );
+
+ gl.uniform1i( uniforms.fogType, 1 );
+ oldFogType = 1;
+ sceneFogType = 1;
+
+ } else if ( fog instanceof THREE.FogExp2 ) {
+
+ gl.uniform1f( uniforms.fogDensity, fog.density );
+
+ gl.uniform1i( uniforms.fogType, 2 );
+ oldFogType = 2;
+ sceneFogType = 2;
+
+ }
+
+ } else {
+
+ gl.uniform1i( uniforms.fogType, 0 );
+ oldFogType = 0;
+ sceneFogType = 0;
+
+ }
+
+
+ // update positions and sort
+
+ for ( var i = 0, l = sprites.length; i < l; i ++ ) {
+
+ var sprite = sprites[ i ];
+
+ sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );
+ sprite.z = - sprite.modelViewMatrix.elements[ 14 ];
+
+ }
+
+ sprites.sort( painterSortStable );
+
+ // render all sprites
+
+ var scale = [];
+
+ for ( var i = 0, l = sprites.length; i < l; i ++ ) {
+
+ var sprite = sprites[ i ];
+ var material = sprite.material;
+
+ gl.uniform1f( uniforms.alphaTest, material.alphaTest );
+ gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements );
+
+ sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale );
+
+ scale[ 0 ] = spriteScale.x;
+ scale[ 1 ] = spriteScale.y;
+
+ var fogType = 0;
+
+ if ( scene.fog && material.fog ) {
+
+ fogType = sceneFogType;
+
+ }
+
+ if ( oldFogType !== fogType ) {
+
+ gl.uniform1i( uniforms.fogType, fogType );
+ oldFogType = fogType;
+
+ }
+
+ if ( material.map !== null ) {
+
+ gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y );
+ gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y );
+
+ } else {
+
+ gl.uniform2f( uniforms.uvOffset, 0, 0 );
+ gl.uniform2f( uniforms.uvScale, 1, 1 );
+
+ }
+
+ gl.uniform1f( uniforms.opacity, material.opacity );
+ gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b );
+
+ gl.uniform1f( uniforms.rotation, material.rotation );
+ gl.uniform2fv( uniforms.scale, scale );
+
+ state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
+ state.setDepthTest( material.depthTest );
+ state.setDepthWrite( material.depthWrite );
+
+ if ( material.map && material.map.image && material.map.image.width ) {
+
+ renderer.setTexture( material.map, 0 );
+
+ } else {
+
+ renderer.setTexture( texture, 0 );
+
+ }
+
+ gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
+
+ }
+
+ // restore gl
+
+ state.enable( gl.CULL_FACE );
+
+ renderer.resetGLState();
+
+ };
+
+ function createProgram () {
+
+ var program = gl.createProgram();
+
+ var vertexShader = gl.createShader( gl.VERTEX_SHADER );
+ var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
+
+ gl.shaderSource( vertexShader, [
+
+ 'precision ' + renderer.getPrecision() + ' float;',
+
+ 'uniform mat4 modelViewMatrix;',
+ 'uniform mat4 projectionMatrix;',
+ 'uniform float rotation;',
+ 'uniform vec2 scale;',
+ 'uniform vec2 uvOffset;',
+ 'uniform vec2 uvScale;',
+
+ 'attribute vec2 position;',
+ 'attribute vec2 uv;',
+
+ 'varying vec2 vUV;',
+
+ 'void main() {',
+
+ 'vUV = uvOffset + uv * uvScale;',
+
+ 'vec2 alignedPosition = position * scale;',
+
+ 'vec2 rotatedPosition;',
+ 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;',
+ 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;',
+
+ 'vec4 finalPosition;',
+
+ 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',
+ 'finalPosition.xy += rotatedPosition;',
+ 'finalPosition = projectionMatrix * finalPosition;',
+
+ 'gl_Position = finalPosition;',
+
+ '}'
+
+ ].join( '\n' ) );
+
+ gl.shaderSource( fragmentShader, [
+
+ 'precision ' + renderer.getPrecision() + ' float;',
+
+ 'uniform vec3 color;',
+ 'uniform sampler2D map;',
+ 'uniform float opacity;',
+
+ 'uniform int fogType;',
+ 'uniform vec3 fogColor;',
+ 'uniform float fogDensity;',
+ 'uniform float fogNear;',
+ 'uniform float fogFar;',
+ 'uniform float alphaTest;',
+
+ 'varying vec2 vUV;',
+
+ 'void main() {',
+
+ 'vec4 texture = texture2D( map, vUV );',
+
+ 'if ( texture.a < alphaTest ) discard;',
+
+ 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );',
+
+ 'if ( fogType > 0 ) {',
+
+ 'float depth = gl_FragCoord.z / gl_FragCoord.w;',
+ 'float fogFactor = 0.0;',
+
+ 'if ( fogType == 1 ) {',
+
+ 'fogFactor = smoothstep( fogNear, fogFar, depth );',
+
+ '} else {',
+
+ 'const float LOG2 = 1.442695;',
+ 'fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );',
+ 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );',
+
+ '}',
+
+ 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );',
+
+ '}',
+
+ '}'
+
+ ].join( '\n' ) );
+
+ gl.compileShader( vertexShader );
+ gl.compileShader( fragmentShader );
+
+ gl.attachShader( program, vertexShader );
+ gl.attachShader( program, fragmentShader );
+
+ gl.linkProgram( program );
+
+ return program;
+
+ }
+
+ function painterSortStable ( a, b ) {
+
+ if ( a.z !== b.z ) {
+
+ return b.z - a.z;
+
+ } else {
+
+ return b.id - a.id;
+
+ }
+
+ }
+
+};
+
+// File:src/extras/CurveUtils.js
+
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ */
+
+THREE.CurveUtils = {
+
+ tangentQuadraticBezier: function ( t, p0, p1, p2 ) {
+
+ return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
+
+ },
+
+ // Puay Bing, thanks for helping with this derivative!
+
+ tangentCubicBezier: function ( t, p0, p1, p2, p3 ) {
+
+ return - 3 * p0 * ( 1 - t ) * ( 1 - t ) +
+ 3 * p1 * ( 1 - t ) * ( 1 - t ) - 6 * t * p1 * ( 1 - t ) +
+ 6 * t * p2 * ( 1 - t ) - 3 * t * t * p2 +
+ 3 * t * t * p3;
+
+ },
+
+ tangentSpline: function ( t, p0, p1, p2, p3 ) {
+
+ // To check if my formulas are correct
+
+ var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1
+ var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t
+ var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2
+ var h11 = 3 * t * t - 2 * t; // t3 − t2
+
+ return h00 + h10 + h01 + h11;
+
+ },
+
+ // Catmull-Rom
+
+ interpolate: function( p0, p1, p2, p3, t ) {
+
+ var v0 = ( p2 - p0 ) * 0.5;
+ var v1 = ( p3 - p1 ) * 0.5;
+ var t2 = t * t;
+ var t3 = t * t2;
+ return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
+
+ }
+
+};
+
+// File:src/extras/GeometryUtils.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.GeometryUtils = {
+
+ merge: function ( geometry1, geometry2, materialIndexOffset ) {
+
+ console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' );
+
+ var matrix;
+
+ if ( geometry2 instanceof THREE.Mesh ) {
+
+ geometry2.matrixAutoUpdate && geometry2.updateMatrix();
+
+ matrix = geometry2.matrix;
+ geometry2 = geometry2.geometry;
+
+ }
+
+ geometry1.merge( geometry2, matrix, materialIndexOffset );
+
+ },
+
+ center: function ( geometry ) {
+
+ console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' );
+ return geometry.center();
+
+ }
+
+};
+
+// File:src/extras/ImageUtils.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author Daosheng Mu / https://github.com/DaoshengMu/
+ */
+
+THREE.ImageUtils = {
+
+ crossOrigin: undefined,
+
+ loadTexture: function ( url, mapping, onLoad, onError ) {
+
+ console.warn( 'THREE.ImageUtils.loadTexture is being deprecated. Use THREE.TextureLoader() instead.' );
+
+ var loader = new THREE.TextureLoader();
+ loader.setCrossOrigin( this.crossOrigin );
+
+ var texture = loader.load( url, onLoad, undefined, onError );
+
+ if ( mapping ) texture.mapping = mapping;
+
+ return texture;
+
+ },
+
+ loadTextureCube: function ( urls, mapping, onLoad, onError ) {
+
+ console.warn( 'THREE.ImageUtils.loadTextureCube is being deprecated. Use THREE.CubeTextureLoader() instead.' );
+
+ var loader = new THREE.CubeTextureLoader();
+ loader.setCrossOrigin( this.crossOrigin );
+
+ var texture = loader.load( urls, onLoad, undefined, onError );
+
+ if ( mapping ) texture.mapping = mapping;
+
+ return texture;
+
+ },
+
+ loadCompressedTexture: function () {
+
+ console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' )
+
+ },
+
+ loadCompressedTextureCube: function () {
+
+ console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' )
+
+ }
+
+};
+
+// File:src/extras/SceneUtils.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SceneUtils = {
+
+ createMultiMaterialObject: function ( geometry, materials ) {
+
+ var group = new THREE.Group();
+
+ for ( var i = 0, l = materials.length; i < l; i ++ ) {
+
+ group.add( new THREE.Mesh( geometry, materials[ i ] ) );
+
+ }
+
+ return group;
+
+ },
+
+ detach: function ( child, parent, scene ) {
+
+ child.applyMatrix( parent.matrixWorld );
+ parent.remove( child );
+ scene.add( child );
+
+ },
+
+ attach: function ( child, scene, parent ) {
+
+ var matrixWorldInverse = new THREE.Matrix4();
+ matrixWorldInverse.getInverse( parent.matrixWorld );
+ child.applyMatrix( matrixWorldInverse );
+
+ scene.remove( child );
+ parent.add( child );
+
+ }
+
+};
+
+// File:src/extras/ShapeUtils.js
+
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ */
+
+THREE.ShapeUtils = {
+
+ // calculate area of the contour polygon
+
+ area: function ( contour ) {
+
+ var n = contour.length;
+ var a = 0.0;
+
+ for ( var p = n - 1, q = 0; q < n; p = q ++ ) {
+
+ a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
+
+ }
+
+ return a * 0.5;
+
+ },
+
+ triangulate: ( function () {
+
+ /**
+ * This code is a quick port of code written in C++ which was submitted to
+ * flipcode.com by John W. Ratcliff // July 22, 2000
+ * See original code and more information here:
+ * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
+ *
+ * ported to actionscript by Zevan Rosser
+ * www.actionsnippet.com
+ *
+ * ported to javascript by Joshua Koo
+ * http://www.lab4games.net/zz85/blog
+ *
+ */
+
+ function snip( contour, u, v, w, n, verts ) {
+
+ var p;
+ var ax, ay, bx, by;
+ var cx, cy, px, py;
+
+ ax = contour[ verts[ u ] ].x;
+ ay = contour[ verts[ u ] ].y;
+
+ bx = contour[ verts[ v ] ].x;
+ by = contour[ verts[ v ] ].y;
+
+ cx = contour[ verts[ w ] ].x;
+ cy = contour[ verts[ w ] ].y;
+
+ if ( Number.EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false;
+
+ var aX, aY, bX, bY, cX, cY;
+ var apx, apy, bpx, bpy, cpx, cpy;
+ var cCROSSap, bCROSScp, aCROSSbp;
+
+ aX = cx - bx; aY = cy - by;
+ bX = ax - cx; bY = ay - cy;
+ cX = bx - ax; cY = by - ay;
+
+ for ( p = 0; p < n; p ++ ) {
+
+ px = contour[ verts[ p ] ].x;
+ py = contour[ verts[ p ] ].y;
+
+ if ( ( ( px === ax ) && ( py === ay ) ) ||
+ ( ( px === bx ) && ( py === by ) ) ||
+ ( ( px === cx ) && ( py === cy ) ) ) continue;
+
+ apx = px - ax; apy = py - ay;
+ bpx = px - bx; bpy = py - by;
+ cpx = px - cx; cpy = py - cy;
+
+ // see if p is inside triangle abc
+
+ aCROSSbp = aX * bpy - aY * bpx;
+ cCROSSap = cX * apy - cY * apx;
+ bCROSScp = bX * cpy - bY * cpx;
+
+ if ( ( aCROSSbp >= - Number.EPSILON ) && ( bCROSScp >= - Number.EPSILON ) && ( cCROSSap >= - Number.EPSILON ) ) return false;
+
+ }
+
+ return true;
+
+ }
+
+ // takes in an contour array and returns
+
+ return function ( contour, indices ) {
+
+ var n = contour.length;
+
+ if ( n < 3 ) return null;
+
+ var result = [],
+ verts = [],
+ vertIndices = [];
+
+ /* we want a counter-clockwise polygon in verts */
+
+ var u, v, w;
+
+ if ( THREE.ShapeUtils.area( contour ) > 0.0 ) {
+
+ for ( v = 0; v < n; v ++ ) verts[ v ] = v;
+
+ } else {
+
+ for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v;
+
+ }
+
+ var nv = n;
+
+ /* remove nv - 2 vertices, creating 1 triangle every time */
+
+ var count = 2 * nv; /* error detection */
+
+ for ( v = nv - 1; nv > 2; ) {
+
+ /* if we loop, it is probably a non-simple polygon */
+
+ if ( ( count -- ) <= 0 ) {
+
+ //** Triangulate: ERROR - probable bad polygon!
+
+ //throw ( "Warning, unable to triangulate polygon!" );
+ //return null;
+ // Sometimes warning is fine, especially polygons are triangulated in reverse.
+ console.warn( 'THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()' );
+
+ if ( indices ) return vertIndices;
+ return result;
+
+ }
+
+ /* three consecutive vertices in current polygon, */
+
+ u = v; if ( nv <= u ) u = 0; /* previous */
+ v = u + 1; if ( nv <= v ) v = 0; /* new v */
+ w = v + 1; if ( nv <= w ) w = 0; /* next */
+
+ if ( snip( contour, u, v, w, nv, verts ) ) {
+
+ var a, b, c, s, t;
+
+ /* true names of the vertices */
+
+ a = verts[ u ];
+ b = verts[ v ];
+ c = verts[ w ];
+
+ /* output Triangle */
+
+ result.push( [ contour[ a ],
+ contour[ b ],
+ contour[ c ] ] );
+
+
+ vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
+
+ /* remove v from the remaining polygon */
+
+ for ( s = v, t = v + 1; t < nv; s ++, t ++ ) {
+
+ verts[ s ] = verts[ t ];
+
+ }
+
+ nv --;
+
+ /* reset error detection counter */
+
+ count = 2 * nv;
+
+ }
+
+ }
+
+ if ( indices ) return vertIndices;
+ return result;
+
+ }
+
+ } )(),
+
+ triangulateShape: function ( contour, holes ) {
+
+ function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) {
+
+ // inOtherPt needs to be collinear to the inSegment
+ if ( inSegPt1.x !== inSegPt2.x ) {
+
+ if ( inSegPt1.x < inSegPt2.x ) {
+
+ return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) );
+
+ } else {
+
+ return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) );
+
+ }
+
+ } else {
+
+ if ( inSegPt1.y < inSegPt2.y ) {
+
+ return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) );
+
+ } else {
+
+ return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) );
+
+ }
+
+ }
+
+ }
+
+ function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) {
+
+ var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y;
+ var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y;
+
+ var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x;
+ var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y;
+
+ var limit = seg1dy * seg2dx - seg1dx * seg2dy;
+ var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy;
+
+ if ( Math.abs( limit ) > Number.EPSILON ) {
+
+ // not parallel
+
+ var perpSeg2;
+ if ( limit > 0 ) {
+
+ if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return [];
+ perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
+ if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return [];
+
+ } else {
+
+ if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return [];
+ perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
+ if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return [];
+
+ }
+
+ // i.e. to reduce rounding errors
+ // intersection at endpoint of segment#1?
+ if ( perpSeg2 === 0 ) {
+
+ if ( ( inExcludeAdjacentSegs ) &&
+ ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return [];
+ return [ inSeg1Pt1 ];
+
+ }
+ if ( perpSeg2 === limit ) {
+
+ if ( ( inExcludeAdjacentSegs ) &&
+ ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return [];
+ return [ inSeg1Pt2 ];
+
+ }
+ // intersection at endpoint of segment#2?
+ if ( perpSeg1 === 0 ) return [ inSeg2Pt1 ];
+ if ( perpSeg1 === limit ) return [ inSeg2Pt2 ];
+
+ // return real intersection point
+ var factorSeg1 = perpSeg2 / limit;
+ return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx,
+ y: inSeg1Pt1.y + factorSeg1 * seg1dy } ];
+
+ } else {
+
+ // parallel or collinear
+ if ( ( perpSeg1 !== 0 ) ||
+ ( seg2dy * seg1seg2dx !== seg2dx * seg1seg2dy ) ) return [];
+
+ // they are collinear or degenerate
+ var seg1Pt = ( ( seg1dx === 0 ) && ( seg1dy === 0 ) ); // segment1 is just a point?
+ var seg2Pt = ( ( seg2dx === 0 ) && ( seg2dy === 0 ) ); // segment2 is just a point?
+ // both segments are points
+ if ( seg1Pt && seg2Pt ) {
+
+ if ( ( inSeg1Pt1.x !== inSeg2Pt1.x ) ||
+ ( inSeg1Pt1.y !== inSeg2Pt1.y ) ) return []; // they are distinct points
+ return [ inSeg1Pt1 ]; // they are the same point
+
+ }
+ // segment#1 is a single point
+ if ( seg1Pt ) {
+
+ if ( ! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2
+ return [ inSeg1Pt1 ];
+
+ }
+ // segment#2 is a single point
+ if ( seg2Pt ) {
+
+ if ( ! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1
+ return [ inSeg2Pt1 ];
+
+ }
+
+ // they are collinear segments, which might overlap
+ var seg1min, seg1max, seg1minVal, seg1maxVal;
+ var seg2min, seg2max, seg2minVal, seg2maxVal;
+ if ( seg1dx !== 0 ) {
+
+ // the segments are NOT on a vertical line
+ if ( inSeg1Pt1.x < inSeg1Pt2.x ) {
+
+ seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x;
+ seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x;
+
+ } else {
+
+ seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x;
+ seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x;
+
+ }
+ if ( inSeg2Pt1.x < inSeg2Pt2.x ) {
+
+ seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x;
+ seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x;
+
+ } else {
+
+ seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x;
+ seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x;
+
+ }
+
+ } else {
+
+ // the segments are on a vertical line
+ if ( inSeg1Pt1.y < inSeg1Pt2.y ) {
+
+ seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y;
+ seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y;
+
+ } else {
+
+ seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y;
+ seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y;
+
+ }
+ if ( inSeg2Pt1.y < inSeg2Pt2.y ) {
+
+ seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y;
+ seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y;
+
+ } else {
+
+ seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y;
+ seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y;
+
+ }
+
+ }
+ if ( seg1minVal <= seg2minVal ) {
+
+ if ( seg1maxVal < seg2minVal ) return [];
+ if ( seg1maxVal === seg2minVal ) {
+
+ if ( inExcludeAdjacentSegs ) return [];
+ return [ seg2min ];
+
+ }
+ if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ];
+ return [ seg2min, seg2max ];
+
+ } else {
+
+ if ( seg1minVal > seg2maxVal ) return [];
+ if ( seg1minVal === seg2maxVal ) {
+
+ if ( inExcludeAdjacentSegs ) return [];
+ return [ seg1min ];
+
+ }
+ if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ];
+ return [ seg1min, seg2max ];
+
+ }
+
+ }
+
+ }
+
+ function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) {
+
+ // The order of legs is important
+
+ // translation of all points, so that Vertex is at (0,0)
+ var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y;
+ var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y;
+ var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y;
+
+ // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg.
+ var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX;
+ var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX;
+
+ if ( Math.abs( from2toAngle ) > Number.EPSILON ) {
+
+ // angle != 180 deg.
+
+ var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX;
+ // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle );
+
+ if ( from2toAngle > 0 ) {
+
+ // main angle < 180 deg.
+ return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) );
+
+ } else {
+
+ // main angle > 180 deg.
+ return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) );
+
+ }
+
+ } else {
+
+ // angle == 180 deg.
+ // console.log( "from2to: 180 deg., from2other: " + from2otherAngle );
+ return ( from2otherAngle > 0 );
+
+ }
+
+ }
+
+
+ function removeHoles( contour, holes ) {
+
+ var shape = contour.concat(); // work on this shape
+ var hole;
+
+ function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) {
+
+ // Check if hole point lies within angle around shape point
+ var lastShapeIdx = shape.length - 1;
+
+ var prevShapeIdx = inShapeIdx - 1;
+ if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx;
+
+ var nextShapeIdx = inShapeIdx + 1;
+ if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0;
+
+ var insideAngle = isPointInsideAngle( shape[ inShapeIdx ], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[ inHoleIdx ] );
+ if ( ! insideAngle ) {
+
+ // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y );
+ return false;
+
+ }
+
+ // Check if shape point lies within angle around hole point
+ var lastHoleIdx = hole.length - 1;
+
+ var prevHoleIdx = inHoleIdx - 1;
+ if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx;
+
+ var nextHoleIdx = inHoleIdx + 1;
+ if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0;
+
+ insideAngle = isPointInsideAngle( hole[ inHoleIdx ], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[ inShapeIdx ] );
+ if ( ! insideAngle ) {
+
+ // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y );
+ return false;
+
+ }
+
+ return true;
+
+ }
+
+ function intersectsShapeEdge( inShapePt, inHolePt ) {
+
+ // checks for intersections with shape edges
+ var sIdx, nextIdx, intersection;
+ for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) {
+
+ nextIdx = sIdx + 1; nextIdx %= shape.length;
+ intersection = intersect_segments_2D( inShapePt, inHolePt, shape[ sIdx ], shape[ nextIdx ], true );
+ if ( intersection.length > 0 ) return true;
+
+ }
+
+ return false;
+
+ }
+
+ var indepHoles = [];
+
+ function intersectsHoleEdge( inShapePt, inHolePt ) {
+
+ // checks for intersections with hole edges
+ var ihIdx, chkHole,
+ hIdx, nextIdx, intersection;
+ for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) {
+
+ chkHole = holes[ indepHoles[ ihIdx ]];
+ for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) {
+
+ nextIdx = hIdx + 1; nextIdx %= chkHole.length;
+ intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[ hIdx ], chkHole[ nextIdx ], true );
+ if ( intersection.length > 0 ) return true;
+
+ }
+
+ }
+ return false;
+
+ }
+
+ var holeIndex, shapeIndex,
+ shapePt, holePt,
+ holeIdx, cutKey, failedCuts = [],
+ tmpShape1, tmpShape2,
+ tmpHole1, tmpHole2;
+
+ for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
+
+ indepHoles.push( h );
+
+ }
+
+ var minShapeIndex = 0;
+ var counter = indepHoles.length * 2;
+ while ( indepHoles.length > 0 ) {
+
+ counter --;
+ if ( counter < 0 ) {
+
+ console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" );
+ break;
+
+ }
+
+ // search for shape-vertex and hole-vertex,
+ // which can be connected without intersections
+ for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) {
+
+ shapePt = shape[ shapeIndex ];
+ holeIndex = - 1;
+
+ // search for hole which can be reached without intersections
+ for ( var h = 0; h < indepHoles.length; h ++ ) {
+
+ holeIdx = indepHoles[ h ];
+
+ // prevent multiple checks
+ cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx;
+ if ( failedCuts[ cutKey ] !== undefined ) continue;
+
+ hole = holes[ holeIdx ];
+ for ( var h2 = 0; h2 < hole.length; h2 ++ ) {
+
+ holePt = hole[ h2 ];
+ if ( ! isCutLineInsideAngles( shapeIndex, h2 ) ) continue;
+ if ( intersectsShapeEdge( shapePt, holePt ) ) continue;
+ if ( intersectsHoleEdge( shapePt, holePt ) ) continue;
+
+ holeIndex = h2;
+ indepHoles.splice( h, 1 );
+
+ tmpShape1 = shape.slice( 0, shapeIndex + 1 );
+ tmpShape2 = shape.slice( shapeIndex );
+ tmpHole1 = hole.slice( holeIndex );
+ tmpHole2 = hole.slice( 0, holeIndex + 1 );
+
+ shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
+
+ minShapeIndex = shapeIndex;
+
+ // Debug only, to show the selected cuts
+ // glob_CutLines.push( [ shapePt, holePt ] );
+
+ break;
+
+ }
+ if ( holeIndex >= 0 ) break; // hole-vertex found
+
+ failedCuts[ cutKey ] = true; // remember failure
+
+ }
+ if ( holeIndex >= 0 ) break; // hole-vertex found
+
+ }
+
+ }
+
+ return shape; /* shape with no holes */
+
+ }
+
+
+ var i, il, f, face,
+ key, index,
+ allPointsMap = {};
+
+ // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first.
+
+ var allpoints = contour.concat();
+
+ for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
+
+ Array.prototype.push.apply( allpoints, holes[ h ] );
+
+ }
+
+ //console.log( "allpoints",allpoints, allpoints.length );
+
+ // prepare all points map
+
+ for ( i = 0, il = allpoints.length; i < il; i ++ ) {
+
+ key = allpoints[ i ].x + ":" + allpoints[ i ].y;
+
+ if ( allPointsMap[ key ] !== undefined ) {
+
+ console.warn( "THREE.Shape: Duplicate point", key );
+
+ }
+
+ allPointsMap[ key ] = i;
+
+ }
+
+ // remove holes by cutting paths to holes and adding them to the shape
+ var shapeWithoutHoles = removeHoles( contour, holes );
+
+ var triangles = THREE.ShapeUtils.triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape
+ //console.log( "triangles",triangles, triangles.length );
+
+ // check all face vertices against all points map
+
+ for ( i = 0, il = triangles.length; i < il; i ++ ) {
+
+ face = triangles[ i ];
+
+ for ( f = 0; f < 3; f ++ ) {
+
+ key = face[ f ].x + ":" + face[ f ].y;
+
+ index = allPointsMap[ key ];
+
+ if ( index !== undefined ) {
+
+ face[ f ] = index;
+
+ }
+
+ }
+
+ }
+
+ return triangles.concat();
+
+ },
+
+ isClockWise: function ( pts ) {
+
+ return THREE.ShapeUtils.area( pts ) < 0;
+
+ },
+
+ // Bezier Curves formulas obtained from
+ // http://en.wikipedia.org/wiki/B%C3%A9zier_curve
+
+ // Quad Bezier Functions
+
+ b2: ( function () {
+
+ function b2p0( t, p ) {
+
+ var k = 1 - t;
+ return k * k * p;
+
+ }
+
+ function b2p1( t, p ) {
+
+ return 2 * ( 1 - t ) * t * p;
+
+ }
+
+ function b2p2( t, p ) {
+
+ return t * t * p;
+
+ }
+
+ return function ( t, p0, p1, p2 ) {
+
+ return b2p0( t, p0 ) + b2p1( t, p1 ) + b2p2( t, p2 );
+
+ };
+
+ } )(),
+
+ // Cubic Bezier Functions
+
+ b3: ( function () {
+
+ function b3p0( t, p ) {
+
+ var k = 1 - t;
+ return k * k * k * p;
+
+ }
+
+ function b3p1( t, p ) {
+
+ var k = 1 - t;
+ return 3 * k * k * t * p;
+
+ }
+
+ function b3p2( t, p ) {
+
+ var k = 1 - t;
+ return 3 * k * t * t * p;
+
+ }
+
+ function b3p3( t, p ) {
+
+ return t * t * t * p;
+
+ }
+
+ return function ( t, p0, p1, p2, p3 ) {
+
+ return b3p0( t, p0 ) + b3p1( t, p1 ) + b3p2( t, p2 ) + b3p3( t, p3 );
+
+ };
+
+ } )()
+
+};
+
+// File:src/extras/audio/Audio.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Audio = function ( listener ) {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'Audio';
+
+ this.context = listener.context;
+ this.source = this.context.createBufferSource();
+ this.source.onended = this.onEnded.bind( this );
+
+ this.gain = this.context.createGain();
+ this.gain.connect( this.context.destination );
+
+ this.panner = this.context.createPanner();
+ this.panner.connect( this.gain );
+
+ this.autoplay = false;
+
+ this.startTime = 0;
+ this.playbackRate = 1;
+ this.isPlaying = false;
+
+};
+
+THREE.Audio.prototype = Object.create( THREE.Object3D.prototype );
+THREE.Audio.prototype.constructor = THREE.Audio;
+
+THREE.Audio.prototype.load = function ( file ) {
+
+ var scope = this;
+
+ var request = new XMLHttpRequest();
+ request.open( 'GET', file, true );
+ request.responseType = 'arraybuffer';
+ request.onload = function ( e ) {
+
+ scope.context.decodeAudioData( this.response, function ( buffer ) {
+
+ scope.source.buffer = buffer;
+
+ if ( scope.autoplay ) scope.play();
+
+ } );
+
+ };
+ request.send();
+
+ return this;
+
+};
+
+THREE.Audio.prototype.play = function () {
+
+ if ( this.isPlaying === true ) {
+
+ console.warn( 'THREE.Audio: Audio is already playing.' );
+ return;
+
+ }
+
+ var source = this.context.createBufferSource();
+
+ source.buffer = this.source.buffer;
+ source.loop = this.source.loop;
+ source.onended = this.source.onended;
+ source.start( 0, this.startTime );
+ source.playbackRate.value = this.playbackRate;
+
+ this.isPlaying = true;
+
+ this.source = source;
+
+ this.connect();
+
+};
+
+THREE.Audio.prototype.pause = function () {
+
+ this.source.stop();
+ this.startTime = this.context.currentTime;
+
+};
+
+THREE.Audio.prototype.stop = function () {
+
+ this.source.stop();
+ this.startTime = 0;
+
+};
+
+THREE.Audio.prototype.connect = function () {
+
+ if ( this.filter !== undefined ) {
+
+ this.source.connect( this.filter );
+ this.filter.connect( this.panner );
+
+ } else {
+
+ this.source.connect( this.panner );
+
+ }
+
+};
+
+THREE.Audio.prototype.disconnect = function () {
+
+ if ( this.filter !== undefined ) {
+
+ this.source.disconnect( this.filter );
+ this.filter.disconnect( this.panner );
+
+ } else {
+
+ this.source.disconnect( this.panner );
+
+ }
+
+};
+
+THREE.Audio.prototype.setFilter = function ( value ) {
+
+ if ( this.isPlaying === true ) {
+
+ this.disconnect();
+ this.filter = value;
+ this.connect();
+
+ } else {
+
+ this.filter = value;
+
+ }
+
+};
+
+THREE.Audio.prototype.getFilter = function () {
+
+ return this.filter;
+
+};
+
+THREE.Audio.prototype.setPlaybackRate = function ( value ) {
+
+ this.playbackRate = value;
+
+ if ( this.isPlaying === true ) {
+
+ this.source.playbackRate.value = this.playbackRate;
+
+ }
+
+};
+
+THREE.Audio.prototype.getPlaybackRate = function () {
+
+ return this.playbackRate;
+
+};
+
+THREE.Audio.prototype.onEnded = function() {
+
+ this.isPlaying = false;
+
+};
+
+THREE.Audio.prototype.setLoop = function ( value ) {
+
+ this.source.loop = value;
+
+};
+
+THREE.Audio.prototype.getLoop = function () {
+
+ return this.source.loop;
+
+};
+
+THREE.Audio.prototype.setRefDistance = function ( value ) {
+
+ this.panner.refDistance = value;
+
+};
+
+THREE.Audio.prototype.getRefDistance = function () {
+
+ return this.panner.refDistance;
+
+};
+
+THREE.Audio.prototype.setRolloffFactor = function ( value ) {
+
+ this.panner.rolloffFactor = value;
+
+};
+
+THREE.Audio.prototype.getRolloffFactor = function () {
+
+ return this.panner.rolloffFactor;
+
+};
+
+THREE.Audio.prototype.setVolume = function ( value ) {
+
+ this.gain.gain.value = value;
+
+};
+
+THREE.Audio.prototype.getVolume = function () {
+
+ return this.gain.gain.value;
+
+};
+
+THREE.Audio.prototype.updateMatrixWorld = ( function () {
+
+ var position = new THREE.Vector3();
+
+ return function updateMatrixWorld( force ) {
+
+ THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
+
+ position.setFromMatrixPosition( this.matrixWorld );
+
+ this.panner.setPosition( position.x, position.y, position.z );
+
+ };
+
+} )();
+
+// File:src/extras/audio/AudioListener.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.AudioListener = function () {
+
+ THREE.Object3D.call( this );
+
+ this.type = 'AudioListener';
+
+ this.context = new ( window.AudioContext || window.webkitAudioContext )();
+
+};
+
+THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype );
+THREE.AudioListener.prototype.constructor = THREE.AudioListener;
+
+THREE.AudioListener.prototype.updateMatrixWorld = ( function () {
+
+ var position = new THREE.Vector3();
+ var quaternion = new THREE.Quaternion();
+ var scale = new THREE.Vector3();
+
+ var orientation = new THREE.Vector3();
+
+ return function updateMatrixWorld( force ) {
+
+ THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
+
+ var listener = this.context.listener;
+ var up = this.up;
+
+ this.matrixWorld.decompose( position, quaternion, scale );
+
+ orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion );
+
+ listener.setPosition( position.x, position.y, position.z );
+ listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z );
+
+ };
+
+} )();
+
+// File:src/extras/core/Curve.js
+
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * Extensible curve object
+ *
+ * Some common of Curve methods
+ * .getPoint(t), getTangent(t)
+ * .getPointAt(u), getTagentAt(u)
+ * .getPoints(), .getSpacedPoints()
+ * .getLength()
+ * .updateArcLengths()
+ *
+ * This following classes subclasses THREE.Curve:
+ *
+ * -- 2d classes --
+ * THREE.LineCurve
+ * THREE.QuadraticBezierCurve
+ * THREE.CubicBezierCurve
+ * THREE.SplineCurve
+ * THREE.ArcCurve
+ * THREE.EllipseCurve
+ *
+ * -- 3d classes --
+ * THREE.LineCurve3
+ * THREE.QuadraticBezierCurve3
+ * THREE.CubicBezierCurve3
+ * THREE.SplineCurve3
+ * THREE.ClosedSplineCurve3
+ *
+ * A series of curves can be represented as a THREE.CurvePath
+ *
+ **/
+
+/**************************************************************
+ * Abstract Curve base class
+ **************************************************************/
+
+THREE.Curve = function () {
+
+};
+
+THREE.Curve.prototype = {
+
+ constructor: THREE.Curve,
+
+ // Virtual base class method to overwrite and implement in subclasses
+ // - t [0 .. 1]
+
+ getPoint: function ( t ) {
+
+ console.warn( "THREE.Curve: Warning, getPoint() not implemented!" );
+ return null;
+
+ },
+
+ // Get point at relative position in curve according to arc length
+ // - u [0 .. 1]
+
+ getPointAt: function ( u ) {
+
+ var t = this.getUtoTmapping( u );
+ return this.getPoint( t );
+
+ },
+
+ // Get sequence of points using getPoint( t )
+
+ getPoints: function ( divisions ) {
+
+ if ( ! divisions ) divisions = 5;
+
+ var d, pts = [];
+
+ for ( d = 0; d <= divisions; d ++ ) {
+
+ pts.push( this.getPoint( d / divisions ) );
+
+ }
+
+ return pts;
+
+ },
+
+ // Get sequence of points using getPointAt( u )
+
+ getSpacedPoints: function ( divisions ) {
+
+ if ( ! divisions ) divisions = 5;
+
+ var d, pts = [];
+
+ for ( d = 0; d <= divisions; d ++ ) {
+
+ pts.push( this.getPointAt( d / divisions ) );
+
+ }
+
+ return pts;
+
+ },
+
+ // Get total curve arc length
+
+ getLength: function () {
+
+ var lengths = this.getLengths();
+ return lengths[ lengths.length - 1 ];
+
+ },
+
+ // Get list of cumulative segment lengths
+
+ getLengths: function ( divisions ) {
+
+ if ( ! divisions ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200;
+
+ if ( this.cacheArcLengths
+ && ( this.cacheArcLengths.length === divisions + 1 )
+ && ! this.needsUpdate ) {
+
+ //console.log( "cached", this.cacheArcLengths );
+ return this.cacheArcLengths;
+
+ }
+
+ this.needsUpdate = false;
+
+ var cache = [];
+ var current, last = this.getPoint( 0 );
+ var p, sum = 0;
+
+ cache.push( 0 );
+
+ for ( p = 1; p <= divisions; p ++ ) {
+
+ current = this.getPoint ( p / divisions );
+ sum += current.distanceTo( last );
+ cache.push( sum );
+ last = current;
+
+ }
+
+ this.cacheArcLengths = cache;
+
+ return cache; // { sums: cache, sum:sum }; Sum is in the last element.
+
+ },
+
+ updateArcLengths: function() {
+
+ this.needsUpdate = true;
+ this.getLengths();
+
+ },
+
+ // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant
+
+ getUtoTmapping: function ( u, distance ) {
+
+ var arcLengths = this.getLengths();
+
+ var i = 0, il = arcLengths.length;
+
+ var targetArcLength; // The targeted u distance value to get
+
+ if ( distance ) {
+
+ targetArcLength = distance;
+
+ } else {
+
+ targetArcLength = u * arcLengths[ il - 1 ];
+
+ }
+
+ //var time = Date.now();
+
+ // binary search for the index with largest value smaller than target u distance
+
+ var low = 0, high = il - 1, comparison;
+
+ while ( low <= high ) {
+
+ i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
+
+ comparison = arcLengths[ i ] - targetArcLength;
+
+ if ( comparison < 0 ) {
+
+ low = i + 1;
+
+ } else if ( comparison > 0 ) {
+
+ high = i - 1;
+
+ } else {
+
+ high = i;
+ break;
+
+ // DONE
+
+ }
+
+ }
+
+ i = high;
+
+ //console.log('b' , i, low, high, Date.now()- time);
+
+ if ( arcLengths[ i ] === targetArcLength ) {
+
+ var t = i / ( il - 1 );
+ return t;
+
+ }
+
+ // we could get finer grain at lengths, or use simple interpolation between two points
+
+ var lengthBefore = arcLengths[ i ];
+ var lengthAfter = arcLengths[ i + 1 ];
+
+ var segmentLength = lengthAfter - lengthBefore;
+
+ // determine where we are between the 'before' and 'after' points
+
+ var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
+
+ // add that fractional amount to t
+
+ var t = ( i + segmentFraction ) / ( il - 1 );
+
+ return t;
+
+ },
+
+ // Returns a unit vector tangent at t
+ // In case any sub curve does not implement its tangent derivation,
+ // 2 points a small delta apart will be used to find its gradient
+ // which seems to give a reasonable approximation
+
+ getTangent: function( t ) {
+
+ var delta = 0.0001;
+ var t1 = t - delta;
+ var t2 = t + delta;
+
+ // Capping in case of danger
+
+ if ( t1 < 0 ) t1 = 0;
+ if ( t2 > 1 ) t2 = 1;
+
+ var pt1 = this.getPoint( t1 );
+ var pt2 = this.getPoint( t2 );
+
+ var vec = pt2.clone().sub( pt1 );
+ return vec.normalize();
+
+ },
+
+ getTangentAt: function ( u ) {
+
+ var t = this.getUtoTmapping( u );
+ return this.getTangent( t );
+
+ }
+
+}
+
+THREE.Curve.Utils = THREE.CurveUtils; // backwards compatibility
+
+// TODO: Transformation for Curves?
+
+/**************************************************************
+ * 3D Curves
+ **************************************************************/
+
+// A Factory method for creating new curve subclasses
+
+THREE.Curve.create = function ( constructor, getPointFunc ) {
+
+ constructor.prototype = Object.create( THREE.Curve.prototype );
+ constructor.prototype.constructor = constructor;
+ constructor.prototype.getPoint = getPointFunc;
+
+ return constructor;
+
+};
+
+// File:src/extras/core/CurvePath.js
+
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ *
+ **/
+
+/**************************************************************
+ * Curved Path - a curve path is simply a array of connected
+ * curves, but retains the api of a curve
+ **************************************************************/
+
+THREE.CurvePath = function () {
+
+ this.curves = [];
+
+ this.autoClose = false; // Automatically closes the path
+
+};
+
+THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype );
+THREE.CurvePath.prototype.constructor = THREE.CurvePath;
+
+THREE.CurvePath.prototype.add = function ( curve ) {
+
+ this.curves.push( curve );
+
+};
+
+/*
+THREE.CurvePath.prototype.checkConnection = function() {
+ // TODO
+ // If the ending of curve is not connected to the starting
+ // or the next curve, then, this is not a real path
+};
+*/
+
+THREE.CurvePath.prototype.closePath = function() {
+
+ // TODO Test
+ // and verify for vector3 (needs to implement equals)
+ // Add a line curve if start and end of lines are not connected
+ var startPoint = this.curves[ 0 ].getPoint( 0 );
+ var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 );
+
+ if ( ! startPoint.equals( endPoint ) ) {
+
+ this.curves.push( new THREE.LineCurve( endPoint, startPoint ) );
+
+ }
+
+};
+
+// To get accurate point with reference to
+// entire path distance at time t,
+// following has to be done:
+
+// 1. Length of each sub path have to be known
+// 2. Locate and identify type of curve
+// 3. Get t for the curve
+// 4. Return curve.getPointAt(t')
+
+THREE.CurvePath.prototype.getPoint = function( t ) {
+
+ var d = t * this.getLength();
+ var curveLengths = this.getCurveLengths();
+ var i = 0;
+
+ // To think about boundaries points.
+
+ while ( i < curveLengths.length ) {
+
+ if ( curveLengths[ i ] >= d ) {
+
+ var diff = curveLengths[ i ] - d;
+ var curve = this.curves[ i ];
+
+ var u = 1 - diff / curve.getLength();
+
+ return curve.getPointAt( u );
+
+ }
+
+ i ++;
+
+ }
+
+ return null;
+
+ // loop where sum != 0, sum > d , sum+1 0 ) {
+
+ laste = points[ points.length - 1 ];
+
+ cpx0 = laste.x;
+ cpy0 = laste.y;
+
+ } else {
+
+ laste = this.actions[ i - 1 ].args;
+
+ cpx0 = laste[ laste.length - 2 ];
+ cpy0 = laste[ laste.length - 1 ];
+
+ }
+
+ for ( var j = 1; j <= divisions; j ++ ) {
+
+ var t = j / divisions;
+
+ tx = b2( t, cpx0, cpx1, cpx );
+ ty = b2( t, cpy0, cpy1, cpy );
+
+ points.push( new THREE.Vector2( tx, ty ) );
+
+ }
+
+ break;
+
+ case 'bezierCurveTo':
+
+ cpx = args[ 4 ];
+ cpy = args[ 5 ];
+
+ cpx1 = args[ 0 ];
+ cpy1 = args[ 1 ];
+
+ cpx2 = args[ 2 ];
+ cpy2 = args[ 3 ];
+
+ if ( points.length > 0 ) {
+
+ laste = points[ points.length - 1 ];
+
+ cpx0 = laste.x;
+ cpy0 = laste.y;
+
+ } else {
+
+ laste = this.actions[ i - 1 ].args;
+
+ cpx0 = laste[ laste.length - 2 ];
+ cpy0 = laste[ laste.length - 1 ];
+
+ }
+
+
+ for ( var j = 1; j <= divisions; j ++ ) {
+
+ var t = j / divisions;
+
+ tx = b3( t, cpx0, cpx1, cpx2, cpx );
+ ty = b3( t, cpy0, cpy1, cpy2, cpy );
+
+ points.push( new THREE.Vector2( tx, ty ) );
+
+ }
+
+ break;
+
+ case 'splineThru':
+
+ laste = this.actions[ i - 1 ].args;
+
+ var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] );
+ var spts = [ last ];
+
+ var n = divisions * args[ 0 ].length;
+
+ spts = spts.concat( args[ 0 ] );
+
+ var spline = new THREE.SplineCurve( spts );
+
+ for ( var j = 1; j <= n; j ++ ) {
+
+ points.push( spline.getPointAt( j / n ) );
+
+ }
+
+ break;
+
+ case 'arc':
+
+ var aX = args[ 0 ], aY = args[ 1 ],
+ aRadius = args[ 2 ],
+ aStartAngle = args[ 3 ], aEndAngle = args[ 4 ],
+ aClockwise = !! args[ 5 ];
+
+ var deltaAngle = aEndAngle - aStartAngle;
+ var angle;
+ var tdivisions = divisions * 2;
+
+ for ( var j = 1; j <= tdivisions; j ++ ) {
+
+ var t = j / tdivisions;
+
+ if ( ! aClockwise ) {
+
+ t = 1 - t;
+
+ }
+
+ angle = aStartAngle + t * deltaAngle;
+
+ tx = aX + aRadius * Math.cos( angle );
+ ty = aY + aRadius * Math.sin( angle );
+
+ //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
+
+ points.push( new THREE.Vector2( tx, ty ) );
+
+ }
+
+ //console.log(points);
+
+ break;
+
+ case 'ellipse':
+
+ var aX = args[ 0 ], aY = args[ 1 ],
+ xRadius = args[ 2 ],
+ yRadius = args[ 3 ],
+ aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
+ aClockwise = !! args[ 6 ],
+ aRotation = args[ 7 ];
+
+
+ var deltaAngle = aEndAngle - aStartAngle;
+ var angle;
+ var tdivisions = divisions * 2;
+
+ var cos, sin;
+ if ( aRotation !== 0 ) {
+
+ cos = Math.cos( aRotation );
+ sin = Math.sin( aRotation );
+
+ }
+
+ for ( var j = 1; j <= tdivisions; j ++ ) {
+
+ var t = j / tdivisions;
+
+ if ( ! aClockwise ) {
+
+ t = 1 - t;
+
+ }
+
+ angle = aStartAngle + t * deltaAngle;
+
+ tx = aX + xRadius * Math.cos( angle );
+ ty = aY + yRadius * Math.sin( angle );
+
+ if ( aRotation !== 0 ) {
+
+ var x = tx, y = ty;
+
+ // Rotate the point about the center of the ellipse.
+ tx = ( x - aX ) * cos - ( y - aY ) * sin + aX;
+ ty = ( x - aX ) * sin + ( y - aY ) * cos + aY;
+
+ }
+
+ //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
+
+ points.push( new THREE.Vector2( tx, ty ) );
+
+ }
+
+ //console.log(points);
+
+ break;
+
+ } // end switch
+
+ }
+
+
+
+ // Normalize to remove the closing point by default.
+ var lastPoint = points[ points.length - 1 ];
+ if ( Math.abs( lastPoint.x - points[ 0 ].x ) < Number.EPSILON &&
+ Math.abs( lastPoint.y - points[ 0 ].y ) < Number.EPSILON )
+ points.splice( points.length - 1, 1 );
+ if ( closedPath ) {
+
+ points.push( points[ 0 ] );
+
+ }
+
+ return points;
+
+};
+
+//
+// Breaks path into shapes
+//
+// Assumptions (if parameter isCCW==true the opposite holds):
+// - solid shapes are defined clockwise (CW)
+// - holes are defined counterclockwise (CCW)
+//
+// If parameter noHoles==true:
+// - all subPaths are regarded as solid shapes
+// - definition order CW/CCW has no relevance
+//
+
+THREE.Path.prototype.toShapes = function( isCCW, noHoles ) {
+
+ function extractSubpaths( inActions ) {
+
+ var subPaths = [], lastPath = new THREE.Path();
+
+ for ( var i = 0, l = inActions.length; i < l; i ++ ) {
+
+ var item = inActions[ i ];
+
+ var args = item.args;
+ var action = item.action;
+
+ if ( action === 'moveTo' ) {
+
+ if ( lastPath.actions.length !== 0 ) {
+
+ subPaths.push( lastPath );
+ lastPath = new THREE.Path();
+
+ }
+
+ }
+
+ lastPath[ action ].apply( lastPath, args );
+
+ }
+
+ if ( lastPath.actions.length !== 0 ) {
+
+ subPaths.push( lastPath );
+
+ }
+
+ // console.log(subPaths);
+
+ return subPaths;
+
+ }
+
+ function toShapesNoHoles( inSubpaths ) {
+
+ var shapes = [];
+
+ for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) {
+
+ var tmpPath = inSubpaths[ i ];
+
+ var tmpShape = new THREE.Shape();
+ tmpShape.actions = tmpPath.actions;
+ tmpShape.curves = tmpPath.curves;
+
+ shapes.push( tmpShape );
+
+ }
+
+ //console.log("shape", shapes);
+
+ return shapes;
+
+ }
+
+ function isPointInsidePolygon( inPt, inPolygon ) {
+
+ var polyLen = inPolygon.length;
+
+ // inPt on polygon contour => immediate success or
+ // toggling of inside/outside at every single! intersection point of an edge
+ // with the horizontal line through inPt, left of inPt
+ // not counting lowerY endpoints of edges and whole edges on that line
+ var inside = false;
+ for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {
+
+ var edgeLowPt = inPolygon[ p ];
+ var edgeHighPt = inPolygon[ q ];
+
+ var edgeDx = edgeHighPt.x - edgeLowPt.x;
+ var edgeDy = edgeHighPt.y - edgeLowPt.y;
+
+ if ( Math.abs( edgeDy ) > Number.EPSILON ) {
+
+ // not parallel
+ if ( edgeDy < 0 ) {
+
+ edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx;
+ edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;
+
+ }
+ if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue;
+
+ if ( inPt.y === edgeLowPt.y ) {
+
+ if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ?
+ // continue; // no intersection or edgeLowPt => doesn't count !!!
+
+ } else {
+
+ var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y );
+ if ( perpEdge === 0 ) return true; // inPt is on contour ?
+ if ( perpEdge < 0 ) continue;
+ inside = ! inside; // true intersection left of inPt
+
+ }
+
+ } else {
+
+ // parallel or collinear
+ if ( inPt.y !== edgeLowPt.y ) continue; // parallel
+ // edge lies on the same horizontal line as inPt
+ if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||
+ ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour !
+ // continue;
+
+ }
+
+ }
+
+ return inside;
+
+ }
+
+ var isClockWise = THREE.ShapeUtils.isClockWise;
+
+ var subPaths = extractSubpaths( this.actions );
+ if ( subPaths.length === 0 ) return [];
+
+ if ( noHoles === true ) return toShapesNoHoles( subPaths );
+
+
+ var solid, tmpPath, tmpShape, shapes = [];
+
+ if ( subPaths.length === 1 ) {
+
+ tmpPath = subPaths[ 0 ];
+ tmpShape = new THREE.Shape();
+ tmpShape.actions = tmpPath.actions;
+ tmpShape.curves = tmpPath.curves;
+ shapes.push( tmpShape );
+ return shapes;
+
+ }
+
+ var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() );
+ holesFirst = isCCW ? ! holesFirst : holesFirst;
+
+ // console.log("Holes first", holesFirst);
+
+ var betterShapeHoles = [];
+ var newShapes = [];
+ var newShapeHoles = [];
+ var mainIdx = 0;
+ var tmpPoints;
+
+ newShapes[ mainIdx ] = undefined;
+ newShapeHoles[ mainIdx ] = [];
+
+ for ( var i = 0, l = subPaths.length; i < l; i ++ ) {
+
+ tmpPath = subPaths[ i ];
+ tmpPoints = tmpPath.getPoints();
+ solid = isClockWise( tmpPoints );
+ solid = isCCW ? ! solid : solid;
+
+ if ( solid ) {
+
+ if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++;
+
+ newShapes[ mainIdx ] = { s: new THREE.Shape(), p: tmpPoints };
+ newShapes[ mainIdx ].s.actions = tmpPath.actions;
+ newShapes[ mainIdx ].s.curves = tmpPath.curves;
+
+ if ( holesFirst ) mainIdx ++;
+ newShapeHoles[ mainIdx ] = [];
+
+ //console.log('cw', i);
+
+ } else {
+
+ newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } );
+
+ //console.log('ccw', i);
+
+ }
+
+ }
+
+ // only Holes? -> probably all Shapes with wrong orientation
+ if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths );
+
+
+ if ( newShapes.length > 1 ) {
+
+ var ambiguous = false;
+ var toChange = [];
+
+ for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
+
+ betterShapeHoles[ sIdx ] = [];
+
+ }
+
+ for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
+
+ var sho = newShapeHoles[ sIdx ];
+
+ for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) {
+
+ var ho = sho[ hIdx ];
+ var hole_unassigned = true;
+
+ for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {
+
+ if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) {
+
+ if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );
+ if ( hole_unassigned ) {
+
+ hole_unassigned = false;
+ betterShapeHoles[ s2Idx ].push( ho );
+
+ } else {
+
+ ambiguous = true;
+
+ }
+
+ }
+
+ }
+ if ( hole_unassigned ) {
+
+ betterShapeHoles[ sIdx ].push( ho );
+
+ }
+
+ }
+
+ }
+ // console.log("ambiguous: ", ambiguous);
+ if ( toChange.length > 0 ) {
+
+ // console.log("to change: ", toChange);
+ if ( ! ambiguous ) newShapeHoles = betterShapeHoles;
+
+ }
+
+ }
+
+ var tmpHoles;
+
+ for ( var i = 0, il = newShapes.length; i < il; i ++ ) {
+
+ tmpShape = newShapes[ i ].s;
+ shapes.push( tmpShape );
+ tmpHoles = newShapeHoles[ i ];
+
+ for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) {
+
+ tmpShape.holes.push( tmpHoles[ j ].h );
+
+ }
+
+ }
+
+ //console.log("shape", shapes);
+
+ return shapes;
+
+};
+
+// File:src/extras/core/Shape.js
+
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * Defines a 2d shape plane using paths.
+ **/
+
+// STEP 1 Create a path.
+// STEP 2 Turn path into shape.
+// STEP 3 ExtrudeGeometry takes in Shape/Shapes
+// STEP 3a - Extract points from each shape, turn to vertices
+// STEP 3b - Triangulate each shape, add faces.
+
+THREE.Shape = function () {
+
+ THREE.Path.apply( this, arguments );
+
+ this.holes = [];
+
+};
+
+THREE.Shape.prototype = Object.create( THREE.Path.prototype );
+THREE.Shape.prototype.constructor = THREE.Shape;
+
+// Convenience method to return ExtrudeGeometry
+
+THREE.Shape.prototype.extrude = function ( options ) {
+
+ return new THREE.ExtrudeGeometry( this, options );
+
+};
+
+// Convenience method to return ShapeGeometry
+
+THREE.Shape.prototype.makeGeometry = function ( options ) {
+
+ return new THREE.ShapeGeometry( this, options );
+
+};
+
+// Get points of holes
+
+THREE.Shape.prototype.getPointsHoles = function ( divisions ) {
+
+ var holesPts = [];
+
+ for ( var i = 0, l = this.holes.length; i < l; i ++ ) {
+
+ holesPts[ i ] = this.holes[ i ].getPoints( divisions );
+
+ }
+
+ return holesPts;
+
+};
+
+
+// Get points of shape and holes (keypoints based on segments parameter)
+
+THREE.Shape.prototype.extractAllPoints = function ( divisions ) {
+
+ return {
+
+ shape: this.getPoints( divisions ),
+ holes: this.getPointsHoles( divisions )
+
+ };
+
+};
+
+THREE.Shape.prototype.extractPoints = function ( divisions ) {
+
+ return this.extractAllPoints( divisions );
+
+};
+
+THREE.Shape.Utils = THREE.ShapeUtils; // backwards compatibility
+
+// File:src/extras/curves/LineCurve.js
+
+/**************************************************************
+ * Line
+ **************************************************************/
+
+THREE.LineCurve = function ( v1, v2 ) {
+
+ this.v1 = v1;
+ this.v2 = v2;
+
+};
+
+THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.LineCurve.prototype.constructor = THREE.LineCurve;
+
+THREE.LineCurve.prototype.getPoint = function ( t ) {
+
+ var point = this.v2.clone().sub( this.v1 );
+ point.multiplyScalar( t ).add( this.v1 );
+
+ return point;
+
+};
+
+// Line curve is linear, so we can overwrite default getPointAt
+
+THREE.LineCurve.prototype.getPointAt = function ( u ) {
+
+ return this.getPoint( u );
+
+};
+
+THREE.LineCurve.prototype.getTangent = function( t ) {
+
+ var tangent = this.v2.clone().sub( this.v1 );
+
+ return tangent.normalize();
+
+};
+
+// File:src/extras/curves/QuadraticBezierCurve.js
+
+/**************************************************************
+ * Quadratic Bezier curve
+ **************************************************************/
+
+
+THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) {
+
+ this.v0 = v0;
+ this.v1 = v1;
+ this.v2 = v2;
+
+};
+
+THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.QuadraticBezierCurve.prototype.constructor = THREE.QuadraticBezierCurve;
+
+
+THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) {
+
+ var b2 = THREE.ShapeUtils.b2;
+
+ return new THREE.Vector2(
+ b2( t, this.v0.x, this.v1.x, this.v2.x ),
+ b2( t, this.v0.y, this.v1.y, this.v2.y )
+ );
+
+};
+
+
+THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) {
+
+ var tangentQuadraticBezier = THREE.CurveUtils.tangentQuadraticBezier;
+
+ return new THREE.Vector2(
+ tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ),
+ tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y )
+ ).normalize();
+
+};
+
+// File:src/extras/curves/CubicBezierCurve.js
+
+/**************************************************************
+ * Cubic Bezier curve
+ **************************************************************/
+
+THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) {
+
+ this.v0 = v0;
+ this.v1 = v1;
+ this.v2 = v2;
+ this.v3 = v3;
+
+};
+
+THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.CubicBezierCurve.prototype.constructor = THREE.CubicBezierCurve;
+
+THREE.CubicBezierCurve.prototype.getPoint = function ( t ) {
+
+ var b3 = THREE.ShapeUtils.b3;
+
+ return new THREE.Vector2(
+ b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ),
+ b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y )
+ );
+
+};
+
+THREE.CubicBezierCurve.prototype.getTangent = function( t ) {
+
+ var tangentCubicBezier = THREE.CurveUtils.tangentCubicBezier;
+
+ return new THREE.Vector2(
+ tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ),
+ tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y )
+ ).normalize();
+
+};
+
+// File:src/extras/curves/SplineCurve.js
+
+/**************************************************************
+ * Spline curve
+ **************************************************************/
+
+THREE.SplineCurve = function ( points /* array of Vector2 */ ) {
+
+ this.points = ( points == undefined ) ? [] : points;
+
+};
+
+THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.SplineCurve.prototype.constructor = THREE.SplineCurve;
+
+THREE.SplineCurve.prototype.getPoint = function ( t ) {
+
+ var points = this.points;
+ var point = ( points.length - 1 ) * t;
+
+ var intPoint = Math.floor( point );
+ var weight = point - intPoint;
+
+ var point0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ];
+ var point1 = points[ intPoint ];
+ var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];
+ var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];
+
+ var interpolate = THREE.CurveUtils.interpolate;
+
+ return new THREE.Vector2(
+ interpolate( point0.x, point1.x, point2.x, point3.x, weight ),
+ interpolate( point0.y, point1.y, point2.y, point3.y, weight )
+ );
+
+};
+
+// File:src/extras/curves/EllipseCurve.js
+
+/**************************************************************
+ * Ellipse curve
+ **************************************************************/
+
+THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {
+
+ this.aX = aX;
+ this.aY = aY;
+
+ this.xRadius = xRadius;
+ this.yRadius = yRadius;
+
+ this.aStartAngle = aStartAngle;
+ this.aEndAngle = aEndAngle;
+
+ this.aClockwise = aClockwise;
+
+ this.aRotation = aRotation || 0;
+
+};
+
+THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.EllipseCurve.prototype.constructor = THREE.EllipseCurve;
+
+THREE.EllipseCurve.prototype.getPoint = function ( t ) {
+
+ var deltaAngle = this.aEndAngle - this.aStartAngle;
+
+ if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2;
+ if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2;
+
+ var angle;
+
+ if ( this.aClockwise === true ) {
+
+ angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle );
+
+ } else {
+
+ angle = this.aStartAngle + t * deltaAngle;
+
+ }
+
+ var x = this.aX + this.xRadius * Math.cos( angle );
+ var y = this.aY + this.yRadius * Math.sin( angle );
+
+ if ( this.aRotation !== 0 ) {
+
+ var cos = Math.cos( this.aRotation );
+ var sin = Math.sin( this.aRotation );
+
+ var tx = x, ty = y;
+
+ // Rotate the point about the center of the ellipse.
+ x = ( tx - this.aX ) * cos - ( ty - this.aY ) * sin + this.aX;
+ y = ( tx - this.aX ) * sin + ( ty - this.aY ) * cos + this.aY;
+
+ }
+
+ return new THREE.Vector2( x, y );
+
+};
+
+// File:src/extras/curves/ArcCurve.js
+
+/**************************************************************
+ * Arc curve
+ **************************************************************/
+
+THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
+
+ THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
+
+};
+
+THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype );
+THREE.ArcCurve.prototype.constructor = THREE.ArcCurve;
+
+// File:src/extras/curves/LineCurve3.js
+
+/**************************************************************
+ * Line3D
+ **************************************************************/
+
+THREE.LineCurve3 = THREE.Curve.create(
+
+ function ( v1, v2 ) {
+
+ this.v1 = v1;
+ this.v2 = v2;
+
+ },
+
+ function ( t ) {
+
+ var vector = new THREE.Vector3();
+
+ vector.subVectors( this.v2, this.v1 ); // diff
+ vector.multiplyScalar( t );
+ vector.add( this.v1 );
+
+ return vector;
+
+ }
+
+);
+
+// File:src/extras/curves/QuadraticBezierCurve3.js
+
+/**************************************************************
+ * Quadratic Bezier 3D curve
+ **************************************************************/
+
+THREE.QuadraticBezierCurve3 = THREE.Curve.create(
+
+ function ( v0, v1, v2 ) {
+
+ this.v0 = v0;
+ this.v1 = v1;
+ this.v2 = v2;
+
+ },
+
+ function ( t ) {
+
+ var b2 = THREE.ShapeUtils.b2;
+
+ return new THREE.Vector3(
+ b2( t, this.v0.x, this.v1.x, this.v2.x ),
+ b2( t, this.v0.y, this.v1.y, this.v2.y ),
+ b2( t, this.v0.z, this.v1.z, this.v2.z )
+ );
+
+ }
+
+);
+
+// File:src/extras/curves/CubicBezierCurve3.js
+
+/**************************************************************
+ * Cubic Bezier 3D curve
+ **************************************************************/
+
+THREE.CubicBezierCurve3 = THREE.Curve.create(
+
+ function ( v0, v1, v2, v3 ) {
+
+ this.v0 = v0;
+ this.v1 = v1;
+ this.v2 = v2;
+ this.v3 = v3;
+
+ },
+
+ function ( t ) {
+
+ var b3 = THREE.ShapeUtils.b3;
+
+ return new THREE.Vector3(
+ b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ),
+ b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ),
+ b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z )
+ );
+
+ }
+
+);
+
+// File:src/extras/curves/SplineCurve3.js
+
+/**************************************************************
+ * Spline 3D curve
+ **************************************************************/
+
+
+THREE.SplineCurve3 = THREE.Curve.create(
+
+ function ( points /* array of Vector3 */ ) {
+
+ console.warn( 'THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3' );
+ this.points = ( points == undefined ) ? [] : points;
+
+ },
+
+ function ( t ) {
+
+ var points = this.points;
+ var point = ( points.length - 1 ) * t;
+
+ var intPoint = Math.floor( point );
+ var weight = point - intPoint;
+
+ var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ];
+ var point1 = points[ intPoint ];
+ var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];
+ var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];
+
+ var interpolate = THREE.CurveUtils.interpolate;
+
+ return new THREE.Vector3(
+ interpolate( point0.x, point1.x, point2.x, point3.x, weight ),
+ interpolate( point0.y, point1.y, point2.y, point3.y, weight ),
+ interpolate( point0.z, point1.z, point2.z, point3.z, weight )
+ );
+
+ }
+
+);
+
+// File:src/extras/curves/CatmullRomCurve3.js
+
+/**
+ * @author zz85 https://github.com/zz85
+ *
+ * Centripetal CatmullRom Curve - which is useful for avoiding
+ * cusps and self-intersections in non-uniform catmull rom curves.
+ * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
+ *
+ * curve.type accepts centripetal(default), chordal and catmullrom
+ * curve.tension is used for catmullrom which defaults to 0.5
+ */
+
+THREE.CatmullRomCurve3 = ( function() {
+
+ var
+ tmp = new THREE.Vector3(),
+ px = new CubicPoly(),
+ py = new CubicPoly(),
+ pz = new CubicPoly();
+
+ /*
+ Based on an optimized c++ solution in
+ - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/
+ - http://ideone.com/NoEbVM
+
+ This CubicPoly class could be used for reusing some variables and calculations,
+ but for three.js curve use, it could be possible inlined and flatten into a single function call
+ which can be placed in CurveUtils.
+ */
+
+ function CubicPoly() {
+
+ }
+
+ /*
+ * Compute coefficients for a cubic polynomial
+ * p(s) = c0 + c1*s + c2*s^2 + c3*s^3
+ * such that
+ * p(0) = x0, p(1) = x1
+ * and
+ * p'(0) = t0, p'(1) = t1.
+ */
+ CubicPoly.prototype.init = function( x0, x1, t0, t1 ) {
+
+ this.c0 = x0;
+ this.c1 = t0;
+ this.c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1;
+ this.c3 = 2 * x0 - 2 * x1 + t0 + t1;
+
+ };
+
+ CubicPoly.prototype.initNonuniformCatmullRom = function( x0, x1, x2, x3, dt0, dt1, dt2 ) {
+
+ // compute tangents when parameterized in [t1,t2]
+ var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1;
+ var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2;
+
+ // rescale tangents for parametrization in [0,1]
+ t1 *= dt1;
+ t2 *= dt1;
+
+ // initCubicPoly
+ this.init( x1, x2, t1, t2 );
+
+ };
+
+ // standard Catmull-Rom spline: interpolate between x1 and x2 with previous/following points x1/x4
+ CubicPoly.prototype.initCatmullRom = function( x0, x1, x2, x3, tension ) {
+
+ this.init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) );
+
+ };
+
+ CubicPoly.prototype.calc = function( t ) {
+
+ var t2 = t * t;
+ var t3 = t2 * t;
+ return this.c0 + this.c1 * t + this.c2 * t2 + this.c3 * t3;
+
+ };
+
+ // Subclass Three.js curve
+ return THREE.Curve.create(
+
+ function ( p /* array of Vector3 */ ) {
+
+ this.points = p || [];
+
+ },
+
+ function ( t ) {
+
+ var points = this.points,
+ point, intPoint, weight, l;
+
+ l = points.length;
+
+ if ( l < 2 ) console.log( 'duh, you need at least 2 points' );
+
+ point = ( l - 1 ) * t;
+ intPoint = Math.floor( point );
+ weight = point - intPoint;
+
+ if ( weight === 0 && intPoint === l - 1 ) {
+
+ intPoint = l - 2;
+ weight = 1;
+
+ }
+
+ var p0, p1, p2, p3;
+
+ if ( intPoint === 0 ) {
+
+ // extrapolate first point
+ tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] );
+ p0 = tmp;
+
+ } else {
+
+ p0 = points[ intPoint - 1 ];
+
+ }
+
+ p1 = points[ intPoint ];
+ p2 = points[ intPoint + 1 ];
+
+ if ( intPoint + 2 < l ) {
+
+ p3 = points[ intPoint + 2 ]
+
+ } else {
+
+ // extrapolate last point
+ tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 2 ] );
+ p3 = tmp;
+
+ }
+
+ if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) {
+
+ // init Centripetal / Chordal Catmull-Rom
+ var pow = this.type === 'chordal' ? 0.5 : 0.25;
+ var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow );
+ var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow );
+ var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow );
+
+ // safety check for repeated points
+ if ( dt1 < 1e-4 ) dt1 = 1.0;
+ if ( dt0 < 1e-4 ) dt0 = dt1;
+ if ( dt2 < 1e-4 ) dt2 = dt1;
+
+ px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 );
+ py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 );
+ pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 );
+
+ } else if ( this.type === 'catmullrom' ) {
+
+ var tension = this.tension !== undefined ? this.tension : 0.5;
+ px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, tension );
+ py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, tension );
+ pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, tension );
+
+ }
+
+ var v = new THREE.Vector3(
+ px.calc( weight ),
+ py.calc( weight ),
+ pz.calc( weight )
+ );
+
+ return v;
+
+ }
+
+ );
+
+} )();
+
+// File:src/extras/curves/ClosedSplineCurve3.js
+
+/**************************************************************
+ * Closed Spline 3D curve
+ **************************************************************/
+
+
+THREE.ClosedSplineCurve3 = THREE.Curve.create(
+
+ function ( points /* array of Vector3 */ ) {
+
+ this.points = ( points == undefined ) ? [] : points;
+
+ },
+
+ function ( t ) {
+
+ var points = this.points;
+ var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1
+
+ var intPoint = Math.floor( point );
+ var weight = point - intPoint;
+
+ intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;
+
+ var point0 = points[ ( intPoint - 1 ) % points.length ];
+ var point1 = points[ ( intPoint ) % points.length ];
+ var point2 = points[ ( intPoint + 1 ) % points.length ];
+ var point3 = points[ ( intPoint + 2 ) % points.length ];
+
+ var interpolate = THREE.CurveUtils.interpolate;
+
+ return new THREE.Vector3(
+ interpolate( point0.x, point1.x, point2.x, point3.x, weight ),
+ interpolate( point0.y, point1.y, point2.y, point3.y, weight ),
+ interpolate( point0.z, point1.z, point2.z, point3.z, weight )
+ );
+
+ }
+
+);
+
+// File:src/extras/geometries/BoxGeometry.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as
+ */
+
+THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'BoxGeometry';
+
+ this.parameters = {
+ width: width,
+ height: height,
+ depth: depth,
+ widthSegments: widthSegments,
+ heightSegments: heightSegments,
+ depthSegments: depthSegments
+ };
+
+ this.widthSegments = widthSegments || 1;
+ this.heightSegments = heightSegments || 1;
+ this.depthSegments = depthSegments || 1;
+
+ var scope = this;
+
+ var width_half = width / 2;
+ var height_half = height / 2;
+ var depth_half = depth / 2;
+
+ buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, 0 ); // px
+ buildPlane( 'z', 'y', 1, - 1, depth, height, - width_half, 1 ); // nx
+ buildPlane( 'x', 'z', 1, 1, width, depth, height_half, 2 ); // py
+ buildPlane( 'x', 'z', 1, - 1, width, depth, - height_half, 3 ); // ny
+ buildPlane( 'x', 'y', 1, - 1, width, height, depth_half, 4 ); // pz
+ buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, 5 ); // nz
+
+ function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) {
+
+ var w, ix, iy,
+ gridX = scope.widthSegments,
+ gridY = scope.heightSegments,
+ width_half = width / 2,
+ height_half = height / 2,
+ offset = scope.vertices.length;
+
+ if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) {
+
+ w = 'z';
+
+ } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) {
+
+ w = 'y';
+ gridY = scope.depthSegments;
+
+ } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) {
+
+ w = 'x';
+ gridX = scope.depthSegments;
+
+ }
+
+ var gridX1 = gridX + 1,
+ gridY1 = gridY + 1,
+ segment_width = width / gridX,
+ segment_height = height / gridY,
+ normal = new THREE.Vector3();
+
+ normal[ w ] = depth > 0 ? 1 : - 1;
+
+ for ( iy = 0; iy < gridY1; iy ++ ) {
+
+ for ( ix = 0; ix < gridX1; ix ++ ) {
+
+ var vector = new THREE.Vector3();
+ vector[ u ] = ( ix * segment_width - width_half ) * udir;
+ vector[ v ] = ( iy * segment_height - height_half ) * vdir;
+ vector[ w ] = depth;
+
+ scope.vertices.push( vector );
+
+ }
+
+ }
+
+ for ( iy = 0; iy < gridY; iy ++ ) {
+
+ for ( ix = 0; ix < gridX; ix ++ ) {
+
+ var a = ix + gridX1 * iy;
+ var b = ix + gridX1 * ( iy + 1 );
+ var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
+ var d = ( ix + 1 ) + gridX1 * iy;
+
+ var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY );
+ var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY );
+ var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY );
+ var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY );
+
+ var face = new THREE.Face3( a + offset, b + offset, d + offset );
+ face.normal.copy( normal );
+ face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
+ face.materialIndex = materialIndex;
+
+ scope.faces.push( face );
+ scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
+
+ face = new THREE.Face3( b + offset, c + offset, d + offset );
+ face.normal.copy( normal );
+ face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
+ face.materialIndex = materialIndex;
+
+ scope.faces.push( face );
+ scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
+
+ }
+
+ }
+
+ }
+
+ this.mergeVertices();
+
+};
+
+THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.BoxGeometry.prototype.constructor = THREE.BoxGeometry;
+
+THREE.BoxGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.BoxGeometry(
+ parameters.width,
+ parameters.height,
+ parameters.depth,
+ parameters.widthSegments,
+ parameters.heightSegments,
+ parameters.depthSegments
+ );
+
+};
+
+THREE.CubeGeometry = THREE.BoxGeometry; // backwards compatibility
+
+// File:src/extras/geometries/CircleGeometry.js
+
+/**
+ * @author hughes
+ */
+
+THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'CircleGeometry';
+
+ this.parameters = {
+ radius: radius,
+ segments: segments,
+ thetaStart: thetaStart,
+ thetaLength: thetaLength
+ };
+
+ this.fromBufferGeometry( new THREE.CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) );
+
+};
+
+THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.CircleGeometry.prototype.constructor = THREE.CircleGeometry;
+
+THREE.CircleGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.CircleGeometry(
+ parameters.radius,
+ parameters.segments,
+ parameters.thetaStart,
+ parameters.thetaLength
+ );
+
+};
+
+// File:src/extras/geometries/CircleBufferGeometry.js
+
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.CircleBufferGeometry = function ( radius, segments, thetaStart, thetaLength ) {
+
+ THREE.BufferGeometry.call( this );
+
+ this.type = 'CircleBufferGeometry';
+
+ this.parameters = {
+ radius: radius,
+ segments: segments,
+ thetaStart: thetaStart,
+ thetaLength: thetaLength
+ };
+
+ radius = radius || 50;
+ segments = segments !== undefined ? Math.max( 3, segments ) : 8;
+
+ thetaStart = thetaStart !== undefined ? thetaStart : 0;
+ thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
+
+ var vertices = segments + 2;
+
+ var positions = new Float32Array( vertices * 3 );
+ var normals = new Float32Array( vertices * 3 );
+ var uvs = new Float32Array( vertices * 2 );
+
+ // center data is already zero, but need to set a few extras
+ normals[ 2 ] = 1.0;
+ uvs[ 0 ] = 0.5;
+ uvs[ 1 ] = 0.5;
+
+ for ( var s = 0, i = 3, ii = 2 ; s <= segments; s ++, i += 3, ii += 2 ) {
+
+ var segment = thetaStart + s / segments * thetaLength;
+
+ positions[ i ] = radius * Math.cos( segment );
+ positions[ i + 1 ] = radius * Math.sin( segment );
+
+ normals[ i + 2 ] = 1; // normal z
+
+ uvs[ ii ] = ( positions[ i ] / radius + 1 ) / 2;
+ uvs[ ii + 1 ] = ( positions[ i + 1 ] / radius + 1 ) / 2;
+
+ }
+
+ var indices = [];
+
+ for ( var i = 1; i <= segments; i ++ ) {
+
+ indices.push( i, i + 1, 0 );
+
+ }
+
+ this.setIndex( new THREE.BufferAttribute( new Uint16Array( indices ), 1 ) );
+ this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
+ this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
+ this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
+
+ this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+
+};
+
+THREE.CircleBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.CircleBufferGeometry.prototype.constructor = THREE.CircleBufferGeometry;
+
+THREE.CircleBufferGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.CircleBufferGeometry(
+ parameters.radius,
+ parameters.segments,
+ parameters.thetaStart,
+ parameters.thetaLength
+ );
+
+};
+
+// File:src/extras/geometries/CylinderGeometry.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'CylinderGeometry';
+
+ this.parameters = {
+ radiusTop: radiusTop,
+ radiusBottom: radiusBottom,
+ height: height,
+ radialSegments: radialSegments,
+ heightSegments: heightSegments,
+ openEnded: openEnded,
+ thetaStart: thetaStart,
+ thetaLength: thetaLength
+ };
+
+ radiusTop = radiusTop !== undefined ? radiusTop : 20;
+ radiusBottom = radiusBottom !== undefined ? radiusBottom : 20;
+ height = height !== undefined ? height : 100;
+
+ radialSegments = radialSegments || 8;
+ heightSegments = heightSegments || 1;
+
+ openEnded = openEnded !== undefined ? openEnded : false;
+ thetaStart = thetaStart !== undefined ? thetaStart : 0;
+ thetaLength = thetaLength !== undefined ? thetaLength : 2 * Math.PI;
+
+ var heightHalf = height / 2;
+
+ var x, y, vertices = [], uvs = [];
+
+ for ( y = 0; y <= heightSegments; y ++ ) {
+
+ var verticesRow = [];
+ var uvsRow = [];
+
+ var v = y / heightSegments;
+ var radius = v * ( radiusBottom - radiusTop ) + radiusTop;
+
+ for ( x = 0; x <= radialSegments; x ++ ) {
+
+ var u = x / radialSegments;
+
+ var vertex = new THREE.Vector3();
+ vertex.x = radius * Math.sin( u * thetaLength + thetaStart );
+ vertex.y = - v * height + heightHalf;
+ vertex.z = radius * Math.cos( u * thetaLength + thetaStart );
+
+ this.vertices.push( vertex );
+
+ verticesRow.push( this.vertices.length - 1 );
+ uvsRow.push( new THREE.Vector2( u, 1 - v ) );
+
+ }
+
+ vertices.push( verticesRow );
+ uvs.push( uvsRow );
+
+ }
+
+ var tanTheta = ( radiusBottom - radiusTop ) / height;
+ var na, nb;
+
+ for ( x = 0; x < radialSegments; x ++ ) {
+
+ if ( radiusTop !== 0 ) {
+
+ na = this.vertices[ vertices[ 0 ][ x ] ].clone();
+ nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone();
+
+ } else {
+
+ na = this.vertices[ vertices[ 1 ][ x ] ].clone();
+ nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone();
+
+ }
+
+ na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize();
+ nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize();
+
+ for ( y = 0; y < heightSegments; y ++ ) {
+
+ var v1 = vertices[ y ][ x ];
+ var v2 = vertices[ y + 1 ][ x ];
+ var v3 = vertices[ y + 1 ][ x + 1 ];
+ var v4 = vertices[ y ][ x + 1 ];
+
+ var n1 = na.clone();
+ var n2 = na.clone();
+ var n3 = nb.clone();
+ var n4 = nb.clone();
+
+ var uv1 = uvs[ y ][ x ].clone();
+ var uv2 = uvs[ y + 1 ][ x ].clone();
+ var uv3 = uvs[ y + 1 ][ x + 1 ].clone();
+ var uv4 = uvs[ y ][ x + 1 ].clone();
+
+ this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
+ this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );
+
+ this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) );
+ this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );
+
+ }
+
+ }
+
+ // top cap
+
+ if ( openEnded === false && radiusTop > 0 ) {
+
+ this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) );
+
+ for ( x = 0; x < radialSegments; x ++ ) {
+
+ var v1 = vertices[ 0 ][ x ];
+ var v2 = vertices[ 0 ][ x + 1 ];
+ var v3 = this.vertices.length - 1;
+
+ var n1 = new THREE.Vector3( 0, 1, 0 );
+ var n2 = new THREE.Vector3( 0, 1, 0 );
+ var n3 = new THREE.Vector3( 0, 1, 0 );
+
+ var uv1 = uvs[ 0 ][ x ].clone();
+ var uv2 = uvs[ 0 ][ x + 1 ].clone();
+ var uv3 = new THREE.Vector2( uv2.x, 0 );
+
+ this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ], undefined, 1 ) );
+ this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
+
+ }
+
+ }
+
+ // bottom cap
+
+ if ( openEnded === false && radiusBottom > 0 ) {
+
+ this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) );
+
+ for ( x = 0; x < radialSegments; x ++ ) {
+
+ var v1 = vertices[ heightSegments ][ x + 1 ];
+ var v2 = vertices[ heightSegments ][ x ];
+ var v3 = this.vertices.length - 1;
+
+ var n1 = new THREE.Vector3( 0, - 1, 0 );
+ var n2 = new THREE.Vector3( 0, - 1, 0 );
+ var n3 = new THREE.Vector3( 0, - 1, 0 );
+
+ var uv1 = uvs[ heightSegments ][ x + 1 ].clone();
+ var uv2 = uvs[ heightSegments ][ x ].clone();
+ var uv3 = new THREE.Vector2( uv2.x, 1 );
+
+ this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ], undefined, 2 ) );
+ this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
+
+ }
+
+ }
+
+ this.computeFaceNormals();
+
+};
+
+THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.CylinderGeometry.prototype.constructor = THREE.CylinderGeometry;
+
+THREE.CylinderGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.CylinderGeometry(
+ parameters.radiusTop,
+ parameters.radiusBottom,
+ parameters.height,
+ parameters.radialSegments,
+ parameters.heightSegments,
+ parameters.openEnded,
+ parameters.thetaStart,
+ parameters.thetaLength
+ );
+
+};
+
+// File:src/extras/geometries/EdgesGeometry.js
+
+/**
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.EdgesGeometry = function ( geometry, thresholdAngle ) {
+
+ THREE.BufferGeometry.call( this );
+
+ thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1;
+
+ var thresholdDot = Math.cos( THREE.Math.degToRad( thresholdAngle ) );
+
+ var edge = [ 0, 0 ], hash = {};
+
+ function sortFunction( a, b ) {
+
+ return a - b;
+
+ }
+
+ var keys = [ 'a', 'b', 'c' ];
+
+ var geometry2;
+
+ if ( geometry instanceof THREE.BufferGeometry ) {
+
+ geometry2 = new THREE.Geometry();
+ geometry2.fromBufferGeometry( geometry );
+
+ } else {
+
+ geometry2 = geometry.clone();
+
+ }
+
+ geometry2.mergeVertices();
+ geometry2.computeFaceNormals();
+
+ var vertices = geometry2.vertices;
+ var faces = geometry2.faces;
+
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
+
+ var face = faces[ i ];
+
+ for ( var j = 0; j < 3; j ++ ) {
+
+ edge[ 0 ] = face[ keys[ j ] ];
+ edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
+ edge.sort( sortFunction );
+
+ var key = edge.toString();
+
+ if ( hash[ key ] === undefined ) {
+
+ hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined };
+
+ } else {
+
+ hash[ key ].face2 = i;
+
+ }
+
+ }
+
+ }
+
+ var coords = [];
+
+ for ( var key in hash ) {
+
+ var h = hash[ key ];
+
+ if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) <= thresholdDot ) {
+
+ var vertex = vertices[ h.vert1 ];
+ coords.push( vertex.x );
+ coords.push( vertex.y );
+ coords.push( vertex.z );
+
+ vertex = vertices[ h.vert2 ];
+ coords.push( vertex.x );
+ coords.push( vertex.y );
+ coords.push( vertex.z );
+
+ }
+
+ }
+
+ this.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( coords ), 3 ) );
+
+};
+
+THREE.EdgesGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.EdgesGeometry.prototype.constructor = THREE.EdgesGeometry;
+
+// File:src/extras/geometries/ExtrudeGeometry.js
+
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ *
+ * Creates extruded geometry from a path shape.
+ *
+ * parameters = {
+ *
+ * curveSegments: , // number of points on the curves
+ * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too
+ * amount: , // Depth to extrude the shape
+ *
+ * bevelEnabled: , // turn on bevel
+ * bevelThickness: , // how deep into the original shape bevel goes
+ * bevelSize: , // how far from shape outline is bevel
+ * bevelSegments: , // number of bevel layers
+ *
+ * extrudePath: // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined)
+ * frames: // containing arrays of tangents, normals, binormals
+ *
+ * uvGenerator: // object that provides UV generator functions
+ *
+ * }
+ **/
+
+THREE.ExtrudeGeometry = function ( shapes, options ) {
+
+ if ( typeof( shapes ) === "undefined" ) {
+
+ shapes = [];
+ return;
+
+ }
+
+ THREE.Geometry.call( this );
+
+ this.type = 'ExtrudeGeometry';
+
+ shapes = Array.isArray( shapes ) ? shapes : [ shapes ];
+
+ this.addShapeList( shapes, options );
+
+ this.computeFaceNormals();
+
+ // can't really use automatic vertex normals
+ // as then front and back sides get smoothed too
+ // should do separate smoothing just for sides
+
+ //this.computeVertexNormals();
+
+ //console.log( "took", ( Date.now() - startTime ) );
+
+};
+
+THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.ExtrudeGeometry.prototype.constructor = THREE.ExtrudeGeometry;
+
+THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) {
+
+ var sl = shapes.length;
+
+ for ( var s = 0; s < sl; s ++ ) {
+
+ var shape = shapes[ s ];
+ this.addShape( shape, options );
+
+ }
+
+};
+
+THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) {
+
+ var amount = options.amount !== undefined ? options.amount : 100;
+
+ var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10
+ var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8
+ var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
+
+ var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false
+
+ var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
+
+ var steps = options.steps !== undefined ? options.steps : 1;
+
+ var extrudePath = options.extrudePath;
+ var extrudePts, extrudeByPath = false;
+
+ // Use default WorldUVGenerator if no UV generators are specified.
+ var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator;
+
+ var splineTube, binormal, normal, position2;
+ if ( extrudePath ) {
+
+ extrudePts = extrudePath.getSpacedPoints( steps );
+
+ extrudeByPath = true;
+ bevelEnabled = false; // bevels not supported for path extrusion
+
+ // SETUP TNB variables
+
+ // Reuse TNB from TubeGeomtry for now.
+ // TODO1 - have a .isClosed in spline?
+
+ splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames( extrudePath, steps, false );
+
+ // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
+
+ binormal = new THREE.Vector3();
+ normal = new THREE.Vector3();
+ position2 = new THREE.Vector3();
+
+ }
+
+ // Safeguards if bevels are not enabled
+
+ if ( ! bevelEnabled ) {
+
+ bevelSegments = 0;
+ bevelThickness = 0;
+ bevelSize = 0;
+
+ }
+
+ // Variables initialization
+
+ var ahole, h, hl; // looping of holes
+ var scope = this;
+
+ var shapesOffset = this.vertices.length;
+
+ var shapePoints = shape.extractPoints( curveSegments );
+
+ var vertices = shapePoints.shape;
+ var holes = shapePoints.holes;
+
+ var reverse = ! THREE.ShapeUtils.isClockWise( vertices );
+
+ if ( reverse ) {
+
+ vertices = vertices.reverse();
+
+ // Maybe we should also check if holes are in the opposite direction, just to be safe ...
+
+ for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+
+ ahole = holes[ h ];
+
+ if ( THREE.ShapeUtils.isClockWise( ahole ) ) {
+
+ holes[ h ] = ahole.reverse();
+
+ }
+
+ }
+
+ reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
+
+ }
+
+
+ var faces = THREE.ShapeUtils.triangulateShape( vertices, holes );
+
+ /* Vertices */
+
+ var contour = vertices; // vertices has all points but contour has only points of circumference
+
+ for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+
+ ahole = holes[ h ];
+
+ vertices = vertices.concat( ahole );
+
+ }
+
+
+ function scalePt2 ( pt, vec, size ) {
+
+ if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" );
+
+ return vec.clone().multiplyScalar( size ).add( pt );
+
+ }
+
+ var b, bs, t, z,
+ vert, vlen = vertices.length,
+ face, flen = faces.length;
+
+
+ // Find directions for point movement
+
+
+ function getBevelVec( inPt, inPrev, inNext ) {
+
+ // computes for inPt the corresponding point inPt' on a new contour
+ // shifted by 1 unit (length of normalized vector) to the left
+ // if we walk along contour clockwise, this new contour is outside the old one
+ //
+ // inPt' is the intersection of the two lines parallel to the two
+ // adjacent edges of inPt at a distance of 1 unit on the left side.
+
+ var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt
+
+ // good reading for geometry algorithms (here: line-line intersection)
+ // http://geomalgorithms.com/a05-_intersect-1.html
+
+ var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y;
+ var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y;
+
+ var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y );
+
+ // check for collinear edges
+ var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x );
+
+ if ( Math.abs( collinear0 ) > Number.EPSILON ) {
+
+ // not collinear
+
+ // length of vectors for normalizing
+
+ var v_prev_len = Math.sqrt( v_prev_lensq );
+ var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y );
+
+ // shift adjacent points by unit vectors to the left
+
+ var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len );
+ var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len );
+
+ var ptNextShift_x = ( inNext.x - v_next_y / v_next_len );
+ var ptNextShift_y = ( inNext.y + v_next_x / v_next_len );
+
+ // scaling factor for v_prev to intersection point
+
+ var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y -
+ ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) /
+ ( v_prev_x * v_next_y - v_prev_y * v_next_x );
+
+ // vector from inPt to intersection point
+
+ v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x );
+ v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y );
+
+ // Don't normalize!, otherwise sharp corners become ugly
+ // but prevent crazy spikes
+ var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y );
+ if ( v_trans_lensq <= 2 ) {
+
+ return new THREE.Vector2( v_trans_x, v_trans_y );
+
+ } else {
+
+ shrink_by = Math.sqrt( v_trans_lensq / 2 );
+
+ }
+
+ } else {
+
+ // handle special case of collinear edges
+
+ var direction_eq = false; // assumes: opposite
+ if ( v_prev_x > Number.EPSILON ) {
+
+ if ( v_next_x > Number.EPSILON ) {
+
+ direction_eq = true;
+
+ }
+
+ } else {
+
+ if ( v_prev_x < - Number.EPSILON ) {
+
+ if ( v_next_x < - Number.EPSILON ) {
+
+ direction_eq = true;
+
+ }
+
+ } else {
+
+ if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) {
+
+ direction_eq = true;
+
+ }
+
+ }
+
+ }
+
+ if ( direction_eq ) {
+
+ // console.log("Warning: lines are a straight sequence");
+ v_trans_x = - v_prev_y;
+ v_trans_y = v_prev_x;
+ shrink_by = Math.sqrt( v_prev_lensq );
+
+ } else {
+
+ // console.log("Warning: lines are a straight spike");
+ v_trans_x = v_prev_x;
+ v_trans_y = v_prev_y;
+ shrink_by = Math.sqrt( v_prev_lensq / 2 );
+
+ }
+
+ }
+
+ return new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by );
+
+ }
+
+
+ var contourMovements = [];
+
+ for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
+
+ if ( j === il ) j = 0;
+ if ( k === il ) k = 0;
+
+ // (j)---(i)---(k)
+ // console.log('i,j,k', i, j , k)
+
+ contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
+
+ }
+
+ var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
+
+ for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+
+ ahole = holes[ h ];
+
+ oneHoleMovements = [];
+
+ for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
+
+ if ( j === il ) j = 0;
+ if ( k === il ) k = 0;
+
+ // (j)---(i)---(k)
+ oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
+
+ }
+
+ holesMovements.push( oneHoleMovements );
+ verticesMovements = verticesMovements.concat( oneHoleMovements );
+
+ }
+
+
+ // Loop bevelSegments, 1 for the front, 1 for the back
+
+ for ( b = 0; b < bevelSegments; b ++ ) {
+
+ //for ( b = bevelSegments; b > 0; b -- ) {
+
+ t = b / bevelSegments;
+ z = bevelThickness * ( 1 - t );
+
+ //z = bevelThickness * t;
+ bs = bevelSize * ( Math.sin ( t * Math.PI / 2 ) ); // curved
+ //bs = bevelSize * t; // linear
+
+ // contract shape
+
+ for ( i = 0, il = contour.length; i < il; i ++ ) {
+
+ vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
+
+ v( vert.x, vert.y, - z );
+
+ }
+
+ // expand holes
+
+ for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+
+ ahole = holes[ h ];
+ oneHoleMovements = holesMovements[ h ];
+
+ for ( i = 0, il = ahole.length; i < il; i ++ ) {
+
+ vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
+
+ v( vert.x, vert.y, - z );
+
+ }
+
+ }
+
+ }
+
+ bs = bevelSize;
+
+ // Back facing vertices
+
+ for ( i = 0; i < vlen; i ++ ) {
+
+ vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
+
+ if ( ! extrudeByPath ) {
+
+ v( vert.x, vert.y, 0 );
+
+ } else {
+
+ // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
+
+ normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x );
+ binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y );
+
+ position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal );
+
+ v( position2.x, position2.y, position2.z );
+
+ }
+
+ }
+
+ // Add stepped vertices...
+ // Including front facing vertices
+
+ var s;
+
+ for ( s = 1; s <= steps; s ++ ) {
+
+ for ( i = 0; i < vlen; i ++ ) {
+
+ vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
+
+ if ( ! extrudeByPath ) {
+
+ v( vert.x, vert.y, amount / steps * s );
+
+ } else {
+
+ // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
+
+ normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x );
+ binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y );
+
+ position2.copy( extrudePts[ s ] ).add( normal ).add( binormal );
+
+ v( position2.x, position2.y, position2.z );
+
+ }
+
+ }
+
+ }
+
+
+ // Add bevel segments planes
+
+ //for ( b = 1; b <= bevelSegments; b ++ ) {
+ for ( b = bevelSegments - 1; b >= 0; b -- ) {
+
+ t = b / bevelSegments;
+ z = bevelThickness * ( 1 - t );
+ //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) );
+ bs = bevelSize * Math.sin ( t * Math.PI / 2 );
+
+ // contract shape
+
+ for ( i = 0, il = contour.length; i < il; i ++ ) {
+
+ vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
+ v( vert.x, vert.y, amount + z );
+
+ }
+
+ // expand holes
+
+ for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+
+ ahole = holes[ h ];
+ oneHoleMovements = holesMovements[ h ];
+
+ for ( i = 0, il = ahole.length; i < il; i ++ ) {
+
+ vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
+
+ if ( ! extrudeByPath ) {
+
+ v( vert.x, vert.y, amount + z );
+
+ } else {
+
+ v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ /* Faces */
+
+ // Top and bottom faces
+
+ buildLidFaces();
+
+ // Sides faces
+
+ buildSideFaces();
+
+
+ ///// Internal functions
+
+ function buildLidFaces() {
+
+ if ( bevelEnabled ) {
+
+ var layer = 0; // steps + 1
+ var offset = vlen * layer;
+
+ // Bottom faces
+
+ for ( i = 0; i < flen; i ++ ) {
+
+ face = faces[ i ];
+ f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset );
+
+ }
+
+ layer = steps + bevelSegments * 2;
+ offset = vlen * layer;
+
+ // Top faces
+
+ for ( i = 0; i < flen; i ++ ) {
+
+ face = faces[ i ];
+ f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );
+
+ }
+
+ } else {
+
+ // Bottom faces
+
+ for ( i = 0; i < flen; i ++ ) {
+
+ face = faces[ i ];
+ f3( face[ 2 ], face[ 1 ], face[ 0 ] );
+
+ }
+
+ // Top faces
+
+ for ( i = 0; i < flen; i ++ ) {
+
+ face = faces[ i ];
+ f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );
+
+ }
+
+ }
+
+ }
+
+ // Create faces for the z-sides of the shape
+
+ function buildSideFaces() {
+
+ var layeroffset = 0;
+ sidewalls( contour, layeroffset );
+ layeroffset += contour.length;
+
+ for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+
+ ahole = holes[ h ];
+ sidewalls( ahole, layeroffset );
+
+ //, true
+ layeroffset += ahole.length;
+
+ }
+
+ }
+
+ function sidewalls( contour, layeroffset ) {
+
+ var j, k;
+ i = contour.length;
+
+ while ( -- i >= 0 ) {
+
+ j = i;
+ k = i - 1;
+ if ( k < 0 ) k = contour.length - 1;
+
+ //console.log('b', i,j, i-1, k,vertices.length);
+
+ var s = 0, sl = steps + bevelSegments * 2;
+
+ for ( s = 0; s < sl; s ++ ) {
+
+ var slen1 = vlen * s;
+ var slen2 = vlen * ( s + 1 );
+
+ var a = layeroffset + j + slen1,
+ b = layeroffset + k + slen1,
+ c = layeroffset + k + slen2,
+ d = layeroffset + j + slen2;
+
+ f4( a, b, c, d, contour, s, sl, j, k );
+
+ }
+
+ }
+
+ }
+
+
+ function v( x, y, z ) {
+
+ scope.vertices.push( new THREE.Vector3( x, y, z ) );
+
+ }
+
+ function f3( a, b, c ) {
+
+ a += shapesOffset;
+ b += shapesOffset;
+ c += shapesOffset;
+
+ scope.faces.push( new THREE.Face3( a, b, c, null, null, 0 ) );
+
+ var uvs = uvgen.generateTopUV( scope, a, b, c );
+
+ scope.faceVertexUvs[ 0 ].push( uvs );
+
+ }
+
+ function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) {
+
+ a += shapesOffset;
+ b += shapesOffset;
+ c += shapesOffset;
+ d += shapesOffset;
+
+ scope.faces.push( new THREE.Face3( a, b, d, null, null, 1 ) );
+ scope.faces.push( new THREE.Face3( b, c, d, null, null, 1 ) );
+
+ var uvs = uvgen.generateSideWallUV( scope, a, b, c, d );
+
+ scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] );
+ scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] );
+
+ }
+
+};
+
+THREE.ExtrudeGeometry.WorldUVGenerator = {
+
+ generateTopUV: function ( geometry, indexA, indexB, indexC ) {
+
+ var vertices = geometry.vertices;
+
+ var a = vertices[ indexA ];
+ var b = vertices[ indexB ];
+ var c = vertices[ indexC ];
+
+ return [
+ new THREE.Vector2( a.x, a.y ),
+ new THREE.Vector2( b.x, b.y ),
+ new THREE.Vector2( c.x, c.y )
+ ];
+
+ },
+
+ generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) {
+
+ var vertices = geometry.vertices;
+
+ var a = vertices[ indexA ];
+ var b = vertices[ indexB ];
+ var c = vertices[ indexC ];
+ var d = vertices[ indexD ];
+
+ if ( Math.abs( a.y - b.y ) < 0.01 ) {
+
+ return [
+ new THREE.Vector2( a.x, 1 - a.z ),
+ new THREE.Vector2( b.x, 1 - b.z ),
+ new THREE.Vector2( c.x, 1 - c.z ),
+ new THREE.Vector2( d.x, 1 - d.z )
+ ];
+
+ } else {
+
+ return [
+ new THREE.Vector2( a.y, 1 - a.z ),
+ new THREE.Vector2( b.y, 1 - b.z ),
+ new THREE.Vector2( c.y, 1 - c.z ),
+ new THREE.Vector2( d.y, 1 - d.z )
+ ];
+
+ }
+
+ }
+};
+
+// File:src/extras/geometries/ShapeGeometry.js
+
+/**
+ * @author jonobr1 / http://jonobr1.com
+ *
+ * Creates a one-sided polygonal geometry from a path shape. Similar to
+ * ExtrudeGeometry.
+ *
+ * parameters = {
+ *
+ * curveSegments: , // number of points on the curves. NOT USED AT THE MOMENT.
+ *
+ * material: // material index for front and back faces
+ * uvGenerator: // object that provides UV generator functions
+ *
+ * }
+ **/
+
+THREE.ShapeGeometry = function ( shapes, options ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'ShapeGeometry';
+
+ if ( Array.isArray( shapes ) === false ) shapes = [ shapes ];
+
+ this.addShapeList( shapes, options );
+
+ this.computeFaceNormals();
+
+};
+
+THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.ShapeGeometry.prototype.constructor = THREE.ShapeGeometry;
+
+/**
+ * Add an array of shapes to THREE.ShapeGeometry.
+ */
+THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) {
+
+ for ( var i = 0, l = shapes.length; i < l; i ++ ) {
+
+ this.addShape( shapes[ i ], options );
+
+ }
+
+ return this;
+
+};
+
+/**
+ * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry.
+ */
+THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) {
+
+ if ( options === undefined ) options = {};
+ var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
+
+ var material = options.material;
+ var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator;
+
+ //
+
+ var i, l, hole;
+
+ var shapesOffset = this.vertices.length;
+ var shapePoints = shape.extractPoints( curveSegments );
+
+ var vertices = shapePoints.shape;
+ var holes = shapePoints.holes;
+
+ var reverse = ! THREE.ShapeUtils.isClockWise( vertices );
+
+ if ( reverse ) {
+
+ vertices = vertices.reverse();
+
+ // Maybe we should also check if holes are in the opposite direction, just to be safe...
+
+ for ( i = 0, l = holes.length; i < l; i ++ ) {
+
+ hole = holes[ i ];
+
+ if ( THREE.ShapeUtils.isClockWise( hole ) ) {
+
+ holes[ i ] = hole.reverse();
+
+ }
+
+ }
+
+ reverse = false;
+
+ }
+
+ var faces = THREE.ShapeUtils.triangulateShape( vertices, holes );
+
+ // Vertices
+
+ for ( i = 0, l = holes.length; i < l; i ++ ) {
+
+ hole = holes[ i ];
+ vertices = vertices.concat( hole );
+
+ }
+
+ //
+
+ var vert, vlen = vertices.length;
+ var face, flen = faces.length;
+
+ for ( i = 0; i < vlen; i ++ ) {
+
+ vert = vertices[ i ];
+
+ this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) );
+
+ }
+
+ for ( i = 0; i < flen; i ++ ) {
+
+ face = faces[ i ];
+
+ var a = face[ 0 ] + shapesOffset;
+ var b = face[ 1 ] + shapesOffset;
+ var c = face[ 2 ] + shapesOffset;
+
+ this.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
+ this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) );
+
+ }
+
+};
+
+// File:src/extras/geometries/LatheGeometry.js
+
+/**
+ * @author astrodud / http://astrodud.isgreat.org/
+ * @author zz85 / https://github.com/zz85
+ * @author bhouston / http://clara.io
+ */
+
+// points - to create a closed torus, one must use a set of points
+// like so: [ a, b, c, d, a ], see first is the same as last.
+// segments - the number of circumference segments to create
+// phiStart - the starting radian
+// phiLength - the radian (0 to 2*PI) range of the lathed section
+// 2*pi is a closed lathe, less than 2PI is a portion.
+
+THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'LatheGeometry';
+
+ this.parameters = {
+ points: points,
+ segments: segments,
+ phiStart: phiStart,
+ phiLength: phiLength
+ };
+
+ segments = segments || 12;
+ phiStart = phiStart || 0;
+ phiLength = phiLength || 2 * Math.PI;
+
+ var inversePointLength = 1.0 / ( points.length - 1 );
+ var inverseSegments = 1.0 / segments;
+
+ for ( var i = 0, il = segments; i <= il; i ++ ) {
+
+ var phi = phiStart + i * inverseSegments * phiLength;
+
+ var c = Math.cos( phi ),
+ s = Math.sin( phi );
+
+ for ( var j = 0, jl = points.length; j < jl; j ++ ) {
+
+ var pt = points[ j ];
+
+ var vertex = new THREE.Vector3();
+
+ vertex.x = c * pt.x - s * pt.y;
+ vertex.y = s * pt.x + c * pt.y;
+ vertex.z = pt.z;
+
+ this.vertices.push( vertex );
+
+ }
+
+ }
+
+ var np = points.length;
+
+ for ( var i = 0, il = segments; i < il; i ++ ) {
+
+ for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) {
+
+ var base = j + np * i;
+ var a = base;
+ var b = base + np;
+ var c = base + 1 + np;
+ var d = base + 1;
+
+ var u0 = i * inverseSegments;
+ var v0 = j * inversePointLength;
+ var u1 = u0 + inverseSegments;
+ var v1 = v0 + inversePointLength;
+
+ this.faces.push( new THREE.Face3( a, b, d ) );
+
+ this.faceVertexUvs[ 0 ].push( [
+
+ new THREE.Vector2( u0, v0 ),
+ new THREE.Vector2( u1, v0 ),
+ new THREE.Vector2( u0, v1 )
+
+ ] );
+
+ this.faces.push( new THREE.Face3( b, c, d ) );
+
+ this.faceVertexUvs[ 0 ].push( [
+
+ new THREE.Vector2( u1, v0 ),
+ new THREE.Vector2( u1, v1 ),
+ new THREE.Vector2( u0, v1 )
+
+ ] );
+
+
+ }
+
+ }
+
+ this.mergeVertices();
+ this.computeFaceNormals();
+ this.computeVertexNormals();
+
+};
+
+THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.LatheGeometry.prototype.constructor = THREE.LatheGeometry;
+
+// File:src/extras/geometries/PlaneGeometry.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
+ */
+
+THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'PlaneGeometry';
+
+ this.parameters = {
+ width: width,
+ height: height,
+ widthSegments: widthSegments,
+ heightSegments: heightSegments
+ };
+
+ this.fromBufferGeometry( new THREE.PlaneBufferGeometry( width, height, widthSegments, heightSegments ) );
+
+};
+
+THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.PlaneGeometry.prototype.constructor = THREE.PlaneGeometry;
+
+THREE.PlaneGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.PlaneGeometry(
+ parameters.width,
+ parameters.height,
+ parameters.widthSegments,
+ parameters.heightSegments
+ );
+
+};
+
+// File:src/extras/geometries/PlaneBufferGeometry.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
+ */
+
+THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegments ) {
+
+ THREE.BufferGeometry.call( this );
+
+ this.type = 'PlaneBufferGeometry';
+
+ this.parameters = {
+ width: width,
+ height: height,
+ widthSegments: widthSegments,
+ heightSegments: heightSegments
+ };
+
+ var width_half = width / 2;
+ var height_half = height / 2;
+
+ var gridX = Math.floor( widthSegments ) || 1;
+ var gridY = Math.floor( heightSegments ) || 1;
+
+ var gridX1 = gridX + 1;
+ var gridY1 = gridY + 1;
+
+ var segment_width = width / gridX;
+ var segment_height = height / gridY;
+
+ var vertices = new Float32Array( gridX1 * gridY1 * 3 );
+ var normals = new Float32Array( gridX1 * gridY1 * 3 );
+ var uvs = new Float32Array( gridX1 * gridY1 * 2 );
+
+ var offset = 0;
+ var offset2 = 0;
+
+ for ( var iy = 0; iy < gridY1; iy ++ ) {
+
+ var y = iy * segment_height - height_half;
+
+ for ( var ix = 0; ix < gridX1; ix ++ ) {
+
+ var x = ix * segment_width - width_half;
+
+ vertices[ offset ] = x;
+ vertices[ offset + 1 ] = - y;
+
+ normals[ offset + 2 ] = 1;
+
+ uvs[ offset2 ] = ix / gridX;
+ uvs[ offset2 + 1 ] = 1 - ( iy / gridY );
+
+ offset += 3;
+ offset2 += 2;
+
+ }
+
+ }
+
+ offset = 0;
+
+ var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 );
+
+ for ( var iy = 0; iy < gridY; iy ++ ) {
+
+ for ( var ix = 0; ix < gridX; ix ++ ) {
+
+ var a = ix + gridX1 * iy;
+ var b = ix + gridX1 * ( iy + 1 );
+ var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
+ var d = ( ix + 1 ) + gridX1 * iy;
+
+ indices[ offset ] = a;
+ indices[ offset + 1 ] = b;
+ indices[ offset + 2 ] = d;
+
+ indices[ offset + 3 ] = b;
+ indices[ offset + 4 ] = c;
+ indices[ offset + 5 ] = d;
+
+ offset += 6;
+
+ }
+
+ }
+
+ this.setIndex( new THREE.BufferAttribute( indices, 1 ) );
+ this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+ this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
+ this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
+
+};
+
+THREE.PlaneBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.PlaneBufferGeometry.prototype.constructor = THREE.PlaneBufferGeometry;
+
+THREE.PlaneBufferGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.PlaneBufferGeometry(
+ parameters.width,
+ parameters.height,
+ parameters.widthSegments,
+ parameters.heightSegments
+ );
+
+};
+
+// File:src/extras/geometries/RingGeometry.js
+
+/**
+ * @author Kaleb Murphy
+ */
+
+THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'RingGeometry';
+
+ this.parameters = {
+ innerRadius: innerRadius,
+ outerRadius: outerRadius,
+ thetaSegments: thetaSegments,
+ phiSegments: phiSegments,
+ thetaStart: thetaStart,
+ thetaLength: thetaLength
+ };
+
+ innerRadius = innerRadius || 0;
+ outerRadius = outerRadius || 50;
+
+ thetaStart = thetaStart !== undefined ? thetaStart : 0;
+ thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
+
+ thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8;
+ phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8;
+
+ var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );
+
+ for ( i = 0; i < phiSegments + 1; i ++ ) {
+
+ // concentric circles inside ring
+
+ for ( o = 0; o < thetaSegments + 1; o ++ ) {
+
+ // number of segments per circle
+
+ var vertex = new THREE.Vector3();
+ var segment = thetaStart + o / thetaSegments * thetaLength;
+ vertex.x = radius * Math.cos( segment );
+ vertex.y = radius * Math.sin( segment );
+
+ this.vertices.push( vertex );
+ uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) );
+
+ }
+
+ radius += radiusStep;
+
+ }
+
+ var n = new THREE.Vector3( 0, 0, 1 );
+
+ for ( i = 0; i < phiSegments; i ++ ) {
+
+ // concentric circles inside ring
+
+ var thetaSegment = i * ( thetaSegments + 1 );
+
+ for ( o = 0; o < thetaSegments ; o ++ ) {
+
+ // number of segments per circle
+
+ var segment = o + thetaSegment;
+
+ var v1 = segment;
+ var v2 = segment + thetaSegments + 1;
+ var v3 = segment + thetaSegments + 2;
+
+ this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
+ this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ] );
+
+ v1 = segment;
+ v2 = segment + thetaSegments + 2;
+ v3 = segment + 1;
+
+ this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
+ this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ] );
+
+ }
+
+ }
+
+ this.computeFaceNormals();
+
+ this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+
+};
+
+THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.RingGeometry.prototype.constructor = THREE.RingGeometry;
+
+THREE.RingGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.RingGeometry(
+ parameters.innerRadius,
+ parameters.outerRadius,
+ parameters.thetaSegments,
+ parameters.phiSegments,
+ parameters.thetaStart,
+ parameters.thetaLength
+ );
+
+};
+
+// File:src/extras/geometries/SphereGeometry.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'SphereGeometry';
+
+ this.parameters = {
+ radius: radius,
+ widthSegments: widthSegments,
+ heightSegments: heightSegments,
+ phiStart: phiStart,
+ phiLength: phiLength,
+ thetaStart: thetaStart,
+ thetaLength: thetaLength
+ };
+
+ this.fromBufferGeometry( new THREE.SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) );
+
+};
+
+THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.SphereGeometry.prototype.constructor = THREE.SphereGeometry;
+
+THREE.SphereGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.SphereGeometry(
+ parameters.radius,
+ parameters.widthSegments,
+ parameters.heightSegments,
+ parameters.phiStart,
+ parameters.phiLength,
+ parameters.thetaStart,
+ parameters.thetaLength
+ );
+
+};
+
+// File:src/extras/geometries/SphereBufferGeometry.js
+
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ * based on THREE.SphereGeometry
+ */
+
+THREE.SphereBufferGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {
+
+ THREE.BufferGeometry.call( this );
+
+ this.type = 'SphereBufferGeometry';
+
+ this.parameters = {
+ radius: radius,
+ widthSegments: widthSegments,
+ heightSegments: heightSegments,
+ phiStart: phiStart,
+ phiLength: phiLength,
+ thetaStart: thetaStart,
+ thetaLength: thetaLength
+ };
+
+ radius = radius || 50;
+
+ widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 );
+ heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 );
+
+ phiStart = phiStart !== undefined ? phiStart : 0;
+ phiLength = phiLength !== undefined ? phiLength : Math.PI * 2;
+
+ thetaStart = thetaStart !== undefined ? thetaStart : 0;
+ thetaLength = thetaLength !== undefined ? thetaLength : Math.PI;
+
+ var thetaEnd = thetaStart + thetaLength;
+
+ var vertexCount = ( ( widthSegments + 1 ) * ( heightSegments + 1 ) );
+
+ var positions = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );
+ var normals = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );
+ var uvs = new THREE.BufferAttribute( new Float32Array( vertexCount * 2 ), 2 );
+
+ var index = 0, vertices = [], normal = new THREE.Vector3();
+
+ for ( var y = 0; y <= heightSegments; y ++ ) {
+
+ var verticesRow = [];
+
+ var v = y / heightSegments;
+
+ for ( var x = 0; x <= widthSegments; x ++ ) {
+
+ var u = x / widthSegments;
+
+ var px = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
+ var py = radius * Math.cos( thetaStart + v * thetaLength );
+ var pz = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
+
+ normal.set( px, py, pz ).normalize();
+
+ positions.setXYZ( index, px, py, pz );
+ normals.setXYZ( index, normal.x, normal.y, normal.z );
+ uvs.setXY( index, u, 1 - v );
+
+ verticesRow.push( index );
+
+ index ++;
+
+ }
+
+ vertices.push( verticesRow );
+
+ }
+
+ var indices = [];
+
+ for ( var y = 0; y < heightSegments; y ++ ) {
+
+ for ( var x = 0; x < widthSegments; x ++ ) {
+
+ var v1 = vertices[ y ][ x + 1 ];
+ var v2 = vertices[ y ][ x ];
+ var v3 = vertices[ y + 1 ][ x ];
+ var v4 = vertices[ y + 1 ][ x + 1 ];
+
+ if ( y !== 0 || thetaStart > 0 ) indices.push( v1, v2, v4 );
+ if ( y !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( v2, v3, v4 );
+
+ }
+
+ }
+
+ this.setIndex( new ( positions.count > 65535 ? THREE.Uint32Attribute : THREE.Uint16Attribute )( indices, 1 ) );
+ this.addAttribute( 'position', positions );
+ this.addAttribute( 'normal', normals );
+ this.addAttribute( 'uv', uvs );
+
+ this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+
+};
+
+THREE.SphereBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.SphereBufferGeometry.prototype.constructor = THREE.SphereBufferGeometry;
+
+THREE.SphereBufferGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.SphereBufferGeometry(
+ parameters.radius,
+ parameters.widthSegments,
+ parameters.heightSegments,
+ parameters.phiStart,
+ parameters.phiLength,
+ parameters.thetaStart,
+ parameters.thetaLength
+ );
+
+};
+
+// File:src/extras/geometries/TorusGeometry.js
+
+/**
+ * @author oosmoxiecode
+ * @author mrdoob / http://mrdoob.com/
+ * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888
+ */
+
+THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'TorusGeometry';
+
+ this.parameters = {
+ radius: radius,
+ tube: tube,
+ radialSegments: radialSegments,
+ tubularSegments: tubularSegments,
+ arc: arc
+ };
+
+ radius = radius || 100;
+ tube = tube || 40;
+ radialSegments = radialSegments || 8;
+ tubularSegments = tubularSegments || 6;
+ arc = arc || Math.PI * 2;
+
+ var center = new THREE.Vector3(), uvs = [], normals = [];
+
+ 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;
+
+ center.x = radius * Math.cos( u );
+ center.y = radius * Math.sin( u );
+
+ var vertex = new THREE.Vector3();
+ vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );
+ vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );
+ vertex.z = tube * Math.sin( v );
+
+ this.vertices.push( vertex );
+
+ uvs.push( new THREE.Vector2( i / tubularSegments, j / radialSegments ) );
+ normals.push( vertex.clone().sub( center ).normalize() );
+
+ }
+
+ }
+
+ 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;
+
+ var face = new THREE.Face3( a, b, d, [ normals[ a ].clone(), normals[ b ].clone(), normals[ d ].clone() ] );
+ this.faces.push( face );
+ this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] );
+
+ face = new THREE.Face3( b, c, d, [ normals[ b ].clone(), normals[ c ].clone(), normals[ d ].clone() ] );
+ this.faces.push( face );
+ this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] );
+
+ }
+
+ }
+
+ this.computeFaceNormals();
+
+};
+
+THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.TorusGeometry.prototype.constructor = THREE.TorusGeometry;
+
+THREE.TorusGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.TorusGeometry(
+ parameters.radius,
+ parameters.tube,
+ parameters.radialSegments,
+ parameters.tubularSegments,
+ parameters.arc
+ );
+
+};
+
+// File:src/extras/geometries/TorusKnotGeometry.js
+
+/**
+ * @author oosmoxiecode
+ * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473
+ */
+
+THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'TorusKnotGeometry';
+
+ this.parameters = {
+ radius: radius,
+ tube: tube,
+ radialSegments: radialSegments,
+ tubularSegments: tubularSegments,
+ p: p,
+ q: q,
+ heightScale: heightScale
+ };
+
+ radius = radius || 100;
+ tube = tube || 40;
+ radialSegments = radialSegments || 64;
+ tubularSegments = tubularSegments || 8;
+ p = p || 2;
+ q = q || 3;
+ heightScale = heightScale || 1;
+
+ var grid = new Array( radialSegments );
+ var tang = new THREE.Vector3();
+ var n = new THREE.Vector3();
+ var bitan = new THREE.Vector3();
+
+ for ( var i = 0; i < radialSegments; ++ i ) {
+
+ grid[ i ] = new Array( tubularSegments );
+ var u = i / radialSegments * 2 * p * Math.PI;
+ var p1 = getPos( u, q, p, radius, heightScale );
+ var p2 = getPos( u + 0.01, q, p, radius, heightScale );
+ tang.subVectors( p2, p1 );
+ n.addVectors( p2, p1 );
+
+ bitan.crossVectors( tang, n );
+ n.crossVectors( bitan, tang );
+ bitan.normalize();
+ n.normalize();
+
+ for ( var j = 0; j < tubularSegments; ++ j ) {
+
+ var v = j / tubularSegments * 2 * Math.PI;
+ var cx = - tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
+ var cy = tube * Math.sin( v );
+
+ var pos = new THREE.Vector3();
+ pos.x = p1.x + cx * n.x + cy * bitan.x;
+ pos.y = p1.y + cx * n.y + cy * bitan.y;
+ pos.z = p1.z + cx * n.z + cy * bitan.z;
+
+ grid[ i ][ j ] = this.vertices.push( pos ) - 1;
+
+ }
+
+ }
+
+ for ( var i = 0; i < radialSegments; ++ i ) {
+
+ for ( var j = 0; j < tubularSegments; ++ j ) {
+
+ var ip = ( i + 1 ) % radialSegments;
+ var jp = ( j + 1 ) % tubularSegments;
+
+ var a = grid[ i ][ j ];
+ var b = grid[ ip ][ j ];
+ var c = grid[ ip ][ jp ];
+ var d = grid[ i ][ jp ];
+
+ var uva = new THREE.Vector2( i / radialSegments, j / tubularSegments );
+ var uvb = new THREE.Vector2( ( i + 1 ) / radialSegments, j / tubularSegments );
+ var uvc = new THREE.Vector2( ( i + 1 ) / radialSegments, ( j + 1 ) / tubularSegments );
+ var uvd = new THREE.Vector2( i / radialSegments, ( j + 1 ) / tubularSegments );
+
+ this.faces.push( new THREE.Face3( a, b, d ) );
+ this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
+
+ this.faces.push( new THREE.Face3( b, c, d ) );
+ this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
+
+ }
+
+ }
+
+ this.computeFaceNormals();
+ this.computeVertexNormals();
+
+ function getPos( u, in_q, in_p, radius, heightScale ) {
+
+ var cu = Math.cos( u );
+ var su = Math.sin( u );
+ var quOverP = in_q / in_p * u;
+ var cs = Math.cos( quOverP );
+
+ var tx = radius * ( 2 + cs ) * 0.5 * cu;
+ var ty = radius * ( 2 + cs ) * su * 0.5;
+ var tz = heightScale * radius * Math.sin( quOverP ) * 0.5;
+
+ return new THREE.Vector3( tx, ty, tz );
+
+ }
+
+};
+
+THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.TorusKnotGeometry.prototype.constructor = THREE.TorusKnotGeometry;
+
+THREE.TorusKnotGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.TorusKnotGeometry(
+ parameters.radius,
+ parameters.tube,
+ parameters.radialSegments,
+ parameters.tubularSegments,
+ parameters.p,
+ parameters.q,
+ parameters.heightScale
+ );
+
+};
+
+// File:src/extras/geometries/TubeGeometry.js
+
+/**
+ * @author WestLangley / https://github.com/WestLangley
+ * @author zz85 / https://github.com/zz85
+ * @author miningold / https://github.com/miningold
+ * @author jonobr1 / https://github.com/jonobr1
+ *
+ * Modified from the TorusKnotGeometry by @oosmoxiecode
+ *
+ * Creates a tube which extrudes along a 3d spline
+ *
+ * Uses parallel transport frames as described in
+ * http://www.cs.indiana.edu/pub/techreports/TR425.pdf
+ */
+
+THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed, taper ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'TubeGeometry';
+
+ this.parameters = {
+ path: path,
+ segments: segments,
+ radius: radius,
+ radialSegments: radialSegments,
+ closed: closed,
+ taper: taper
+ };
+
+ segments = segments || 64;
+ radius = radius || 1;
+ radialSegments = radialSegments || 8;
+ closed = closed || false;
+ taper = taper || THREE.TubeGeometry.NoTaper;
+
+ var grid = [];
+
+ var scope = this,
+
+ tangent,
+ normal,
+ binormal,
+
+ numpoints = segments + 1,
+
+ u, v, r,
+
+ cx, cy,
+ pos, pos2 = new THREE.Vector3(),
+ i, j,
+ ip, jp,
+ a, b, c, d,
+ uva, uvb, uvc, uvd;
+
+ var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ),
+ tangents = frames.tangents,
+ normals = frames.normals,
+ binormals = frames.binormals;
+
+ // proxy internals
+ this.tangents = tangents;
+ this.normals = normals;
+ this.binormals = binormals;
+
+ function vert( x, y, z ) {
+
+ return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1;
+
+ }
+
+ // construct the grid
+
+ for ( i = 0; i < numpoints; i ++ ) {
+
+ grid[ i ] = [];
+
+ u = i / ( numpoints - 1 );
+
+ pos = path.getPointAt( u );
+
+ tangent = tangents[ i ];
+ normal = normals[ i ];
+ binormal = binormals[ i ];
+
+ r = radius * taper( u );
+
+ for ( j = 0; j < radialSegments; j ++ ) {
+
+ v = j / radialSegments * 2 * Math.PI;
+
+ cx = - r * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
+ cy = r * Math.sin( v );
+
+ pos2.copy( pos );
+ pos2.x += cx * normal.x + cy * binormal.x;
+ pos2.y += cx * normal.y + cy * binormal.y;
+ pos2.z += cx * normal.z + cy * binormal.z;
+
+ grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z );
+
+ }
+
+ }
+
+
+ // construct the mesh
+
+ for ( i = 0; i < segments; i ++ ) {
+
+ for ( j = 0; j < radialSegments; j ++ ) {
+
+ ip = ( closed ) ? ( i + 1 ) % segments : i + 1;
+ jp = ( j + 1 ) % radialSegments;
+
+ a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! ***
+ b = grid[ ip ][ j ];
+ c = grid[ ip ][ jp ];
+ d = grid[ i ][ jp ];
+
+ uva = new THREE.Vector2( i / segments, j / radialSegments );
+ uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments );
+ uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments );
+ uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments );
+
+ this.faces.push( new THREE.Face3( a, b, d ) );
+ this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
+
+ this.faces.push( new THREE.Face3( b, c, d ) );
+ this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
+
+ }
+
+ }
+
+ this.computeFaceNormals();
+ this.computeVertexNormals();
+
+};
+
+THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.TubeGeometry.prototype.constructor = THREE.TubeGeometry;
+THREE.TubeGeometry.prototype.clone = function() {
+
+ return new this.constructor( this.parameters.path,
+ this.parameters.segments, this.parameters.radius, this.parameters.radialSegments,
+ this.parameters.closed, this.parameters.taper
+ );
+
+};
+
+THREE.TubeGeometry.NoTaper = function ( u ) {
+
+ return 1;
+
+};
+
+THREE.TubeGeometry.SinusoidalTaper = function ( u ) {
+
+ return Math.sin( Math.PI * u );
+
+};
+
+// For computing of Frenet frames, exposing the tangents, normals and binormals the spline
+THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) {
+
+ var normal = new THREE.Vector3(),
+
+ tangents = [],
+ normals = [],
+ binormals = [],
+
+ vec = new THREE.Vector3(),
+ mat = new THREE.Matrix4(),
+
+ numpoints = segments + 1,
+ theta,
+ smallest,
+
+ tx, ty, tz,
+ i, u;
+
+
+ // expose internals
+ this.tangents = tangents;
+ this.normals = normals;
+ this.binormals = binormals;
+
+ // compute the tangent vectors for each segment on the path
+
+ for ( i = 0; i < numpoints; i ++ ) {
+
+ u = i / ( numpoints - 1 );
+
+ tangents[ i ] = path.getTangentAt( u );
+ tangents[ i ].normalize();
+
+ }
+
+ initialNormal3();
+
+ /*
+ function initialNormal1(lastBinormal) {
+ // fixed start binormal. Has dangers of 0 vectors
+ normals[ 0 ] = new THREE.Vector3();
+ binormals[ 0 ] = new THREE.Vector3();
+ if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 );
+ normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize();
+ binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
+ }
+
+ function initialNormal2() {
+
+ // This uses the Frenet-Serret formula for deriving binormal
+ var t2 = path.getTangentAt( epsilon );
+
+ normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize();
+ binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] );
+
+ normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent
+ binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
+
+ }
+ */
+
+ function initialNormal3() {
+
+ // select an initial normal vector perpendicular to the first tangent vector,
+ // and in the direction of the smallest tangent xyz component
+
+ normals[ 0 ] = new THREE.Vector3();
+ binormals[ 0 ] = new THREE.Vector3();
+ smallest = Number.MAX_VALUE;
+ tx = Math.abs( tangents[ 0 ].x );
+ ty = Math.abs( tangents[ 0 ].y );
+ tz = Math.abs( tangents[ 0 ].z );
+
+ if ( tx <= smallest ) {
+
+ smallest = tx;
+ normal.set( 1, 0, 0 );
+
+ }
+
+ if ( ty <= smallest ) {
+
+ smallest = ty;
+ normal.set( 0, 1, 0 );
+
+ }
+
+ if ( tz <= smallest ) {
+
+ normal.set( 0, 0, 1 );
+
+ }
+
+ vec.crossVectors( tangents[ 0 ], normal ).normalize();
+
+ normals[ 0 ].crossVectors( tangents[ 0 ], vec );
+ binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );
+
+ }
+
+
+ // compute the slowly-varying normal and binormal vectors for each segment on the path
+
+ for ( i = 1; i < numpoints; i ++ ) {
+
+ normals[ i ] = normals[ i - 1 ].clone();
+
+ binormals[ i ] = binormals[ i - 1 ].clone();
+
+ vec.crossVectors( tangents[ i - 1 ], tangents[ i ] );
+
+ if ( vec.length() > Number.EPSILON ) {
+
+ vec.normalize();
+
+ theta = Math.acos( THREE.Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors
+
+ normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );
+
+ }
+
+ binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
+
+ }
+
+
+ // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
+
+ if ( closed ) {
+
+ theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints - 1 ] ), - 1, 1 ) );
+ theta /= ( numpoints - 1 );
+
+ if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints - 1 ] ) ) > 0 ) {
+
+ theta = - theta;
+
+ }
+
+ for ( i = 1; i < numpoints; i ++ ) {
+
+ // twist a little...
+ normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
+ binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
+
+ }
+
+ }
+
+};
+
+// File:src/extras/geometries/PolyhedronGeometry.js
+
+/**
+ * @author clockworkgeek / https://github.com/clockworkgeek
+ * @author timothypratley / https://github.com/timothypratley
+ * @author WestLangley / http://github.com/WestLangley
+*/
+
+THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'PolyhedronGeometry';
+
+ this.parameters = {
+ vertices: vertices,
+ indices: indices,
+ radius: radius,
+ detail: detail
+ };
+
+ radius = radius || 1;
+ detail = detail || 0;
+
+ var that = this;
+
+ for ( var i = 0, l = vertices.length; i < l; i += 3 ) {
+
+ prepare( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) );
+
+ }
+
+ var p = this.vertices;
+
+ var faces = [];
+
+ for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) {
+
+ var v1 = p[ indices[ i ] ];
+ var v2 = p[ indices[ i + 1 ] ];
+ var v3 = p[ indices[ i + 2 ] ];
+
+ faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ], undefined, j );
+
+ }
+
+ var centroid = new THREE.Vector3();
+
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
+
+ subdivide( faces[ i ], detail );
+
+ }
+
+
+ // Handle case when face straddles the seam
+
+ for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) {
+
+ var uvs = this.faceVertexUvs[ 0 ][ i ];
+
+ var x0 = uvs[ 0 ].x;
+ var x1 = uvs[ 1 ].x;
+ var x2 = uvs[ 2 ].x;
+
+ var max = Math.max( x0, x1, x2 );
+ var min = Math.min( x0, x1, x2 );
+
+ if ( max > 0.9 && min < 0.1 ) {
+
+ // 0.9 is somewhat arbitrary
+
+ if ( x0 < 0.2 ) uvs[ 0 ].x += 1;
+ if ( x1 < 0.2 ) uvs[ 1 ].x += 1;
+ if ( x2 < 0.2 ) uvs[ 2 ].x += 1;
+
+ }
+
+ }
+
+
+ // Apply radius
+
+ for ( var i = 0, l = this.vertices.length; i < l; i ++ ) {
+
+ this.vertices[ i ].multiplyScalar( radius );
+
+ }
+
+
+ // Merge vertices
+
+ this.mergeVertices();
+
+ this.computeFaceNormals();
+
+ this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+
+
+ // Project vector onto sphere's surface
+
+ function prepare( vector ) {
+
+ var vertex = vector.normalize().clone();
+ vertex.index = that.vertices.push( vertex ) - 1;
+
+ // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle.
+
+ var u = azimuth( vector ) / 2 / Math.PI + 0.5;
+ var v = inclination( vector ) / Math.PI + 0.5;
+ vertex.uv = new THREE.Vector2( u, 1 - v );
+
+ return vertex;
+
+ }
+
+
+ // Approximate a curved face with recursively sub-divided triangles.
+
+ function make( v1, v2, v3, materialIndex ) {
+
+ var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ], undefined, materialIndex );
+ that.faces.push( face );
+
+ centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 );
+
+ var azi = azimuth( centroid );
+
+ that.faceVertexUvs[ 0 ].push( [
+ correctUV( v1.uv, v1, azi ),
+ correctUV( v2.uv, v2, azi ),
+ correctUV( v3.uv, v3, azi )
+ ] );
+
+ }
+
+
+ // Analytically subdivide a face to the required detail level.
+
+ function subdivide( face, detail ) {
+
+ var cols = Math.pow( 2, detail );
+ var a = prepare( that.vertices[ face.a ] );
+ var b = prepare( that.vertices[ face.b ] );
+ var c = prepare( that.vertices[ face.c ] );
+ var v = [];
+
+ var materialIndex = face.materialIndex;
+
+ // Construct all of the vertices for this subdivision.
+
+ for ( var i = 0 ; i <= cols; i ++ ) {
+
+ v[ i ] = [];
+
+ var aj = prepare( a.clone().lerp( c, i / cols ) );
+ var bj = prepare( b.clone().lerp( c, i / cols ) );
+ var rows = cols - i;
+
+ for ( var j = 0; j <= rows; j ++ ) {
+
+ if ( j === 0 && i === cols ) {
+
+ v[ i ][ j ] = aj;
+
+ } else {
+
+ v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) );
+
+ }
+
+ }
+
+ }
+
+ // Construct all of the faces.
+
+ for ( var i = 0; i < cols ; i ++ ) {
+
+ for ( var j = 0; j < 2 * ( cols - i ) - 1; j ++ ) {
+
+ var k = Math.floor( j / 2 );
+
+ if ( j % 2 === 0 ) {
+
+ make(
+ v[ i ][ k + 1 ],
+ v[ i + 1 ][ k ],
+ v[ i ][ k ],
+ materialIndex
+ );
+
+ } else {
+
+ make(
+ v[ i ][ k + 1 ],
+ v[ i + 1 ][ k + 1 ],
+ v[ i + 1 ][ k ],
+ materialIndex
+ );
+
+ }
+
+ }
+
+ }
+
+ }
+
+
+ // Angle around the Y axis, counter-clockwise when looking from above.
+
+ function azimuth( vector ) {
+
+ return Math.atan2( vector.z, - vector.x );
+
+ }
+
+
+ // Angle above the XZ plane.
+
+ function inclination( vector ) {
+
+ return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );
+
+ }
+
+
+ // Texture fixing helper. Spheres have some odd behaviours.
+
+ function correctUV( uv, vector, azimuth ) {
+
+ if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y );
+ if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y );
+ return uv.clone();
+
+ }
+
+
+};
+
+THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.PolyhedronGeometry.prototype.constructor = THREE.PolyhedronGeometry;
+
+THREE.PolyhedronGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.PolyhedronGeometry(
+ parameters.vertices,
+ parameters.indices,
+ parameters.radius,
+ parameters.detail
+ );
+
+};
+
+// File:src/extras/geometries/DodecahedronGeometry.js
+
+/**
+ * @author Abe Pazos / https://hamoid.com
+ */
+
+THREE.DodecahedronGeometry = function ( radius, detail ) {
+
+ var t = ( 1 + Math.sqrt( 5 ) ) / 2;
+ var r = 1 / t;
+
+ var vertices = [
+
+ // (±1, ±1, ±1)
+ - 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, ±1/φ, ±φ)
+ 0, - r, - t, 0, - r, t,
+ 0, r, - t, 0, r, t,
+
+ // (±1/φ, ±φ, 0)
+ - r, - t, 0, - r, t, 0,
+ r, - t, 0, r, t, 0,
+
+ // (±φ, 0, ±1/φ)
+ - t, 0, - r, t, 0, - r,
+ - t, 0, r, t, 0, r
+ ];
+
+ var indices = [
+ 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
+ ];
+
+ THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
+
+ this.type = 'DodecahedronGeometry';
+
+ this.parameters = {
+ radius: radius,
+ detail: detail
+ };
+
+};
+
+THREE.DodecahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype );
+THREE.DodecahedronGeometry.prototype.constructor = THREE.DodecahedronGeometry;
+
+THREE.DodecahedronGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.DodecahedronGeometry(
+ parameters.radius,
+ parameters.detail
+ );
+
+};
+
+// File:src/extras/geometries/IcosahedronGeometry.js
+
+/**
+ * @author timothypratley / https://github.com/timothypratley
+ */
+
+THREE.IcosahedronGeometry = function ( radius, detail ) {
+
+ var t = ( 1 + Math.sqrt( 5 ) ) / 2;
+
+ var vertices = [
+ - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0,
+ 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t,
+ t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1
+ ];
+
+ var indices = [
+ 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
+ ];
+
+ THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
+
+ this.type = 'IcosahedronGeometry';
+
+ this.parameters = {
+ radius: radius,
+ detail: detail
+ };
+
+};
+
+THREE.IcosahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype );
+THREE.IcosahedronGeometry.prototype.constructor = THREE.IcosahedronGeometry;
+
+THREE.IcosahedronGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.IcosahedronGeometry(
+ parameters.radius,
+ parameters.detail
+ );
+
+};
+
+// File:src/extras/geometries/OctahedronGeometry.js
+
+/**
+ * @author timothypratley / https://github.com/timothypratley
+ */
+
+THREE.OctahedronGeometry = function ( radius, detail ) {
+
+ var vertices = [
+ 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1
+ ];
+
+ var indices = [
+ 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2
+ ];
+
+ THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
+
+ this.type = 'OctahedronGeometry';
+
+ this.parameters = {
+ radius: radius,
+ detail: detail
+ };
+
+};
+
+THREE.OctahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype );
+THREE.OctahedronGeometry.prototype.constructor = THREE.OctahedronGeometry;
+
+THREE.OctahedronGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.OctahedronGeometry(
+ parameters.radius,
+ parameters.detail
+ );
+
+};
+
+// File:src/extras/geometries/TetrahedronGeometry.js
+
+/**
+ * @author timothypratley / https://github.com/timothypratley
+ */
+
+THREE.TetrahedronGeometry = function ( radius, detail ) {
+
+ var vertices = [
+ 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1
+ ];
+
+ var indices = [
+ 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1
+ ];
+
+ THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
+
+ this.type = 'TetrahedronGeometry';
+
+ this.parameters = {
+ radius: radius,
+ detail: detail
+ };
+
+};
+
+THREE.TetrahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype );
+THREE.TetrahedronGeometry.prototype.constructor = THREE.TetrahedronGeometry;
+
+THREE.TetrahedronGeometry.prototype.clone = function () {
+
+ var parameters = this.parameters;
+
+ return new THREE.TetrahedronGeometry(
+ parameters.radius,
+ parameters.detail
+ );
+
+};
+
+// File:src/extras/geometries/ParametricGeometry.js
+
+/**
+ * @author zz85 / https://github.com/zz85
+ * Parametric Surfaces Geometry
+ * based on the brilliant article by @prideout http://prideout.net/blog/?p=44
+ *
+ * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements );
+ *
+ */
+
+THREE.ParametricGeometry = function ( func, slices, stacks ) {
+
+ THREE.Geometry.call( this );
+
+ this.type = 'ParametricGeometry';
+
+ this.parameters = {
+ func: func,
+ slices: slices,
+ stacks: stacks
+ };
+
+ var verts = this.vertices;
+ var faces = this.faces;
+ var uvs = this.faceVertexUvs[ 0 ];
+
+ var i, j, p;
+ var u, v;
+
+ var sliceCount = slices + 1;
+
+ for ( i = 0; i <= stacks; i ++ ) {
+
+ v = i / stacks;
+
+ for ( j = 0; j <= slices; j ++ ) {
+
+ u = j / slices;
+
+ p = func( u, v );
+ verts.push( p );
+
+ }
+
+ }
+
+ var a, b, c, d;
+ var uva, uvb, uvc, uvd;
+
+ for ( i = 0; i < stacks; i ++ ) {
+
+ for ( j = 0; j < slices; j ++ ) {
+
+ a = i * sliceCount + j;
+ b = i * sliceCount + j + 1;
+ c = ( i + 1 ) * sliceCount + j + 1;
+ d = ( i + 1 ) * sliceCount + j;
+
+ uva = new THREE.Vector2( j / slices, i / stacks );
+ uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks );
+ uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks );
+ uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks );
+
+ faces.push( new THREE.Face3( a, b, d ) );
+ uvs.push( [ uva, uvb, uvd ] );
+
+ faces.push( new THREE.Face3( b, c, d ) );
+ uvs.push( [ uvb.clone(), uvc, uvd.clone() ] );
+
+ }
+
+ }
+
+ // console.log(this);
+
+ // magic bullet
+ // var diff = this.mergeVertices();
+ // console.log('removed ', diff, ' vertices by merging');
+
+ this.computeFaceNormals();
+ this.computeVertexNormals();
+
+};
+
+THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.ParametricGeometry.prototype.constructor = THREE.ParametricGeometry;
+
+// File:src/extras/geometries/WireframeGeometry.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.WireframeGeometry = function ( geometry ) {
+
+ THREE.BufferGeometry.call( this );
+
+ var edge = [ 0, 0 ], hash = {};
+
+ function sortFunction( a, b ) {
+
+ return a - b;
+
+ }
+
+ var keys = [ 'a', 'b', 'c' ];
+
+ if ( geometry instanceof THREE.Geometry ) {
+
+ var vertices = geometry.vertices;
+ var faces = geometry.faces;
+ var numEdges = 0;
+
+ // allocate maximal size
+ var edges = new Uint32Array( 6 * faces.length );
+
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
+
+ var face = faces[ i ];
+
+ for ( var j = 0; j < 3; j ++ ) {
+
+ edge[ 0 ] = face[ keys[ j ] ];
+ edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
+ edge.sort( sortFunction );
+
+ var key = edge.toString();
+
+ if ( hash[ key ] === undefined ) {
+
+ edges[ 2 * numEdges ] = edge[ 0 ];
+ edges[ 2 * numEdges + 1 ] = edge[ 1 ];
+ hash[ key ] = true;
+ numEdges ++;
+
+ }
+
+ }
+
+ }
+
+ var coords = new Float32Array( numEdges * 2 * 3 );
+
+ for ( var i = 0, l = numEdges; i < l; i ++ ) {
+
+ for ( var j = 0; j < 2; j ++ ) {
+
+ var vertex = vertices[ edges [ 2 * i + j ] ];
+
+ var index = 6 * i + 3 * j;
+ coords[ index + 0 ] = vertex.x;
+ coords[ index + 1 ] = vertex.y;
+ coords[ index + 2 ] = vertex.z;
+
+ }
+
+ }
+
+ this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
+
+ } else if ( geometry instanceof THREE.BufferGeometry ) {
+
+ if ( geometry.index !== null ) {
+
+ // Indexed BufferGeometry
+
+ var indices = geometry.index.array;
+ var vertices = geometry.attributes.position;
+ var drawcalls = geometry.drawcalls;
+ var numEdges = 0;
+
+ if ( drawcalls.length === 0 ) {
+
+ geometry.addGroup( 0, indices.length );
+
+ }
+
+ // allocate maximal size
+ var edges = new Uint32Array( 2 * indices.length );
+
+ for ( var o = 0, ol = drawcalls.length; o < ol; ++ o ) {
+
+ var drawcall = drawcalls[ o ];
+
+ var start = drawcall.start;
+ var count = drawcall.count;
+
+ for ( var i = start, il = start + count; i < il; i += 3 ) {
+
+ for ( var j = 0; j < 3; j ++ ) {
+
+ edge[ 0 ] = indices[ i + j ];
+ edge[ 1 ] = indices[ i + ( j + 1 ) % 3 ];
+ edge.sort( sortFunction );
+
+ var key = edge.toString();
+
+ if ( hash[ key ] === undefined ) {
+
+ edges[ 2 * numEdges ] = edge[ 0 ];
+ edges[ 2 * numEdges + 1 ] = edge[ 1 ];
+ hash[ key ] = true;
+ numEdges ++;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ var coords = new Float32Array( numEdges * 2 * 3 );
+
+ for ( var i = 0, l = numEdges; i < l; i ++ ) {
+
+ for ( var j = 0; j < 2; j ++ ) {
+
+ var index = 6 * i + 3 * j;
+ var index2 = edges[ 2 * i + j ];
+
+ coords[ index + 0 ] = vertices.getX( index2 );
+ coords[ index + 1 ] = vertices.getY( index2 );
+ coords[ index + 2 ] = vertices.getZ( index2 );
+
+ }
+
+ }
+
+ this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
+
+ } else {
+
+ // non-indexed BufferGeometry
+
+ var vertices = geometry.attributes.position.array;
+ var numEdges = vertices.length / 3;
+ var numTris = numEdges / 3;
+
+ var coords = new Float32Array( numEdges * 2 * 3 );
+
+ for ( var i = 0, l = numTris; i < l; i ++ ) {
+
+ for ( var j = 0; j < 3; j ++ ) {
+
+ var index = 18 * i + 6 * j;
+
+ var index1 = 9 * i + 3 * j;
+ coords[ index + 0 ] = vertices[ index1 ];
+ coords[ index + 1 ] = vertices[ index1 + 1 ];
+ coords[ index + 2 ] = vertices[ index1 + 2 ];
+
+ var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 );
+ coords[ index + 3 ] = vertices[ index2 ];
+ coords[ index + 4 ] = vertices[ index2 + 1 ];
+ coords[ index + 5 ] = vertices[ index2 + 2 ];
+
+ }
+
+ }
+
+ this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
+
+ }
+
+ }
+
+};
+
+THREE.WireframeGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.WireframeGeometry.prototype.constructor = THREE.WireframeGeometry;
+
+// File:src/extras/helpers/AxisHelper.js
+
+/**
+ * @author sroucheray / http://sroucheray.org/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.AxisHelper = function ( size ) {
+
+ size = size || 1;
+
+ var vertices = new Float32Array( [
+ 0, 0, 0, size, 0, 0,
+ 0, 0, 0, 0, size, 0,
+ 0, 0, 0, 0, 0, size
+ ] );
+
+ var colors = new Float32Array( [
+ 1, 0, 0, 1, 0.6, 0,
+ 0, 1, 0, 0.6, 1, 0,
+ 0, 0, 1, 0, 0.6, 1
+ ] );
+
+ var geometry = new THREE.BufferGeometry();
+ geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+ geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
+
+ var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
+
+ THREE.LineSegments.call( this, geometry, material );
+
+};
+
+THREE.AxisHelper.prototype = Object.create( THREE.LineSegments.prototype );
+THREE.AxisHelper.prototype.constructor = THREE.AxisHelper;
+
+// File:src/extras/helpers/ArrowHelper.js
+
+/**
+ * @author WestLangley / http://github.com/WestLangley
+ * @author zz85 / http://github.com/zz85
+ * @author bhouston / http://clara.io
+ *
+ * Creates an arrow for visualizing directions
+ *
+ * Parameters:
+ * dir - Vector3
+ * origin - Vector3
+ * length - Number
+ * color - color in hex value
+ * headLength - Number
+ * headWidth - Number
+ */
+
+THREE.ArrowHelper = ( function () {
+
+ var lineGeometry = new THREE.Geometry();
+ lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) );
+
+ var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 );
+ coneGeometry.translate( 0, - 0.5, 0 );
+
+ return function ArrowHelper( dir, origin, length, color, headLength, headWidth ) {
+
+ // dir is assumed to be normalized
+
+ THREE.Object3D.call( this );
+
+ if ( color === undefined ) color = 0xffff00;
+ if ( length === undefined ) length = 1;
+ if ( headLength === undefined ) headLength = 0.2 * length;
+ if ( headWidth === undefined ) headWidth = 0.2 * headLength;
+
+ this.position.copy( origin );
+
+ if ( headLength < length ) {
+ this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) );
+ this.line.matrixAutoUpdate = false;
+ this.add( this.line );
+ }
+
+ this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) );
+ this.cone.matrixAutoUpdate = false;
+ this.add( this.cone );
+
+ this.setDirection( dir );
+ this.setLength( length, headLength, headWidth );
+
+ }
+
+}() );
+
+THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype );
+THREE.ArrowHelper.prototype.constructor = THREE.ArrowHelper;
+
+THREE.ArrowHelper.prototype.setDirection = ( function () {
+
+ var axis = new THREE.Vector3();
+ var radians;
+
+ return function setDirection( dir ) {
+
+ // dir is assumed to be normalized
+
+ if ( dir.y > 0.99999 ) {
+
+ this.quaternion.set( 0, 0, 0, 1 );
+
+ } else if ( dir.y < - 0.99999 ) {
+
+ this.quaternion.set( 1, 0, 0, 0 );
+
+ } else {
+
+ axis.set( dir.z, 0, - dir.x ).normalize();
+
+ radians = Math.acos( dir.y );
+
+ this.quaternion.setFromAxisAngle( axis, radians );
+
+ }
+
+ };
+
+}() );
+
+THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) {
+
+ if ( headLength === undefined ) headLength = 0.2 * length;
+ if ( headWidth === undefined ) headWidth = 0.2 * headLength;
+
+ if ( headLength < length ){
+ this.line.scale.set( 1, length - headLength, 1 );
+ this.line.updateMatrix();
+ }
+
+ this.cone.scale.set( headWidth, headLength, headWidth );
+ this.cone.position.y = length;
+ this.cone.updateMatrix();
+
+};
+
+THREE.ArrowHelper.prototype.setColor = function ( color ) {
+
+ if ( this.line !== undefined ) this.line.material.color.set( color );
+ this.cone.material.color.set( color );
+
+};
+
+// File:src/extras/helpers/BoxHelper.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.BoxHelper = function ( object ) {
+
+ var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );
+ var positions = new Float32Array( 8 * 3 );
+
+ var geometry = new THREE.BufferGeometry();
+ geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
+ geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
+
+ THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ) );
+
+ if ( object !== undefined ) {
+
+ this.update( object );
+
+ }
+
+};
+
+THREE.BoxHelper.prototype = Object.create( THREE.LineSegments.prototype );
+THREE.BoxHelper.prototype.constructor = THREE.BoxHelper;
+
+THREE.BoxHelper.prototype.update = ( function () {
+
+ var box = new THREE.Box3();
+
+ return function ( object ) {
+
+ box.setFromObject( object );
+
+ if ( box.empty() ) return;
+
+ var min = box.min;
+ var max = box.max;
+
+ /*
+ 5____4
+ 1/___0/|
+ | 6__|_7
+ 2/___3/
+
+ 0: max.x, max.y, max.z
+ 1: min.x, max.y, max.z
+ 2: min.x, min.y, max.z
+ 3: max.x, min.y, max.z
+ 4: max.x, max.y, min.z
+ 5: min.x, max.y, min.z
+ 6: min.x, min.y, min.z
+ 7: max.x, min.y, min.z
+ */
+
+ var position = this.geometry.attributes.position;
+ var array = position.array;
+
+ array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z;
+ array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z;
+ array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z;
+ array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z;
+ array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z;
+ array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z;
+ array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z;
+ array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z;
+
+ position.needsUpdate = true;
+
+ this.geometry.computeBoundingSphere();
+
+ }
+
+} )();
+
+// File:src/extras/helpers/BoundingBoxHelper.js
+
+/**
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+// a helper to show the world-axis-aligned bounding box for an object
+
+THREE.BoundingBoxHelper = function ( object, hex ) {
+
+ var color = ( hex !== undefined ) ? hex : 0x888888;
+
+ this.object = object;
+
+ this.box = new THREE.Box3();
+
+ THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) );
+
+};
+
+THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype );
+THREE.BoundingBoxHelper.prototype.constructor = THREE.BoundingBoxHelper;
+
+THREE.BoundingBoxHelper.prototype.update = function () {
+
+ this.box.setFromObject( this.object );
+
+ this.box.size( this.scale );
+
+ this.box.center( this.position );
+
+};
+
+// File:src/extras/helpers/CameraHelper.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * - shows frustum, line of sight and up of the camera
+ * - suitable for fast updates
+ * - based on frustum visualization in lightgl.js shadowmap example
+ * http://evanw.github.com/lightgl.js/tests/shadowmap.html
+ */
+
+THREE.CameraHelper = function ( camera ) {
+
+ var geometry = new THREE.Geometry();
+ var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } );
+
+ var pointMap = {};
+
+ // colors
+
+ var hexFrustum = 0xffaa00;
+ var hexCone = 0xff0000;
+ var hexUp = 0x00aaff;
+ var hexTarget = 0xffffff;
+ var hexCross = 0x333333;
+
+ // near
+
+ addLine( "n1", "n2", hexFrustum );
+ addLine( "n2", "n4", hexFrustum );
+ addLine( "n4", "n3", hexFrustum );
+ addLine( "n3", "n1", hexFrustum );
+
+ // far
+
+ addLine( "f1", "f2", hexFrustum );
+ addLine( "f2", "f4", hexFrustum );
+ addLine( "f4", "f3", hexFrustum );
+ addLine( "f3", "f1", hexFrustum );
+
+ // sides
+
+ addLine( "n1", "f1", hexFrustum );
+ addLine( "n2", "f2", hexFrustum );
+ addLine( "n3", "f3", hexFrustum );
+ addLine( "n4", "f4", hexFrustum );
+
+ // cone
+
+ addLine( "p", "n1", hexCone );
+ addLine( "p", "n2", hexCone );
+ addLine( "p", "n3", hexCone );
+ addLine( "p", "n4", hexCone );
+
+ // up
+
+ addLine( "u1", "u2", hexUp );
+ addLine( "u2", "u3", hexUp );
+ addLine( "u3", "u1", hexUp );
+
+ // target
+
+ addLine( "c", "t", hexTarget );
+ addLine( "p", "c", hexCross );
+
+ // cross
+
+ addLine( "cn1", "cn2", hexCross );
+ addLine( "cn3", "cn4", hexCross );
+
+ addLine( "cf1", "cf2", hexCross );
+ addLine( "cf3", "cf4", hexCross );
+
+ function addLine( a, b, hex ) {
+
+ addPoint( a, hex );
+ addPoint( b, hex );
+
+ }
+
+ function addPoint( id, hex ) {
+
+ geometry.vertices.push( new THREE.Vector3() );
+ geometry.colors.push( new THREE.Color( hex ) );
+
+ if ( pointMap[ id ] === undefined ) {
+
+ pointMap[ id ] = [];
+
+ }
+
+ pointMap[ id ].push( geometry.vertices.length - 1 );
+
+ }
+
+ THREE.LineSegments.call( this, geometry, material );
+
+ this.camera = camera;
+ this.camera.updateProjectionMatrix();
+
+ this.matrix = camera.matrixWorld;
+ this.matrixAutoUpdate = false;
+
+ this.pointMap = pointMap;
+
+ this.update();
+
+};
+
+THREE.CameraHelper.prototype = Object.create( THREE.LineSegments.prototype );
+THREE.CameraHelper.prototype.constructor = THREE.CameraHelper;
+
+THREE.CameraHelper.prototype.update = function () {
+
+ var geometry, pointMap;
+
+ var vector = new THREE.Vector3();
+ var camera = new THREE.Camera();
+
+ function setPoint( point, x, y, z ) {
+
+ vector.set( x, y, z ).unproject( camera );
+
+ var points = pointMap[ point ];
+
+ if ( points !== undefined ) {
+
+ for ( var i = 0, il = points.length; i < il; i ++ ) {
+
+ geometry.vertices[ points[ i ] ].copy( vector );
+
+ }
+
+ }
+
+ }
+
+ return function () {
+
+ geometry = this.geometry;
+ pointMap = this.pointMap;
+
+ var w = 1, h = 1;
+
+ // we need just camera projection matrix
+ // world matrix must be identity
+
+ camera.projectionMatrix.copy( this.camera.projectionMatrix );
+
+ // center / target
+
+ setPoint( "c", 0, 0, - 1 );
+ setPoint( "t", 0, 0, 1 );
+
+ // near
+
+ setPoint( "n1", - w, - h, - 1 );
+ setPoint( "n2", w, - h, - 1 );
+ setPoint( "n3", - w, h, - 1 );
+ setPoint( "n4", w, h, - 1 );
+
+ // far
+
+ setPoint( "f1", - w, - h, 1 );
+ setPoint( "f2", w, - h, 1 );
+ setPoint( "f3", - w, h, 1 );
+ setPoint( "f4", w, h, 1 );
+
+ // up
+
+ setPoint( "u1", w * 0.7, h * 1.1, - 1 );
+ setPoint( "u2", - w * 0.7, h * 1.1, - 1 );
+ setPoint( "u3", 0, h * 2, - 1 );
+
+ // cross
+
+ setPoint( "cf1", - w, 0, 1 );
+ setPoint( "cf2", w, 0, 1 );
+ setPoint( "cf3", 0, - h, 1 );
+ setPoint( "cf4", 0, h, 1 );
+
+ setPoint( "cn1", - w, 0, - 1 );
+ setPoint( "cn2", w, 0, - 1 );
+ setPoint( "cn3", 0, - h, - 1 );
+ setPoint( "cn4", 0, h, - 1 );
+
+ geometry.verticesNeedUpdate = true;
+
+ };
+
+}();
+
+// File:src/extras/helpers/DirectionalLightHelper.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.DirectionalLightHelper = function ( light, size ) {
+
+ THREE.Object3D.call( this );
+
+ this.light = light;
+ this.light.updateMatrixWorld();
+
+ this.matrix = light.matrixWorld;
+ this.matrixAutoUpdate = false;
+
+ size = size || 1;
+
+ var geometry = new THREE.Geometry();
+ geometry.vertices.push(
+ new THREE.Vector3( - size, size, 0 ),
+ new THREE.Vector3( size, size, 0 ),
+ new THREE.Vector3( size, - size, 0 ),
+ new THREE.Vector3( - size, - size, 0 ),
+ new THREE.Vector3( - size, size, 0 )
+ );
+
+ var material = new THREE.LineBasicMaterial( { fog: false } );
+ material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+
+ this.lightPlane = new THREE.Line( geometry, material );
+ this.add( this.lightPlane );
+
+ geometry = new THREE.Geometry();
+ geometry.vertices.push(
+ new THREE.Vector3(),
+ new THREE.Vector3()
+ );
+
+ material = new THREE.LineBasicMaterial( { fog: false } );
+ material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+
+ this.targetLine = new THREE.Line( geometry, material );
+ this.add( this.targetLine );
+
+ this.update();
+
+};
+
+THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+THREE.DirectionalLightHelper.prototype.constructor = THREE.DirectionalLightHelper;
+
+THREE.DirectionalLightHelper.prototype.dispose = function () {
+
+ this.lightPlane.geometry.dispose();
+ this.lightPlane.material.dispose();
+ this.targetLine.geometry.dispose();
+ this.targetLine.material.dispose();
+
+};
+
+THREE.DirectionalLightHelper.prototype.update = function () {
+
+ var v1 = new THREE.Vector3();
+ var v2 = new THREE.Vector3();
+ var v3 = new THREE.Vector3();
+
+ return function () {
+
+ v1.setFromMatrixPosition( this.light.matrixWorld );
+ v2.setFromMatrixPosition( this.light.target.matrixWorld );
+ v3.subVectors( v2, v1 );
+
+ this.lightPlane.lookAt( v3 );
+ this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+
+ this.targetLine.geometry.vertices[ 1 ].copy( v3 );
+ this.targetLine.geometry.verticesNeedUpdate = true;
+ this.targetLine.material.color.copy( this.lightPlane.material.color );
+
+ };
+
+}();
+
+// File:src/extras/helpers/EdgesHelper.js
+
+/**
+ * @author WestLangley / http://github.com/WestLangley
+ * @param object THREE.Mesh whose geometry will be used
+ * @param hex line color
+ * @param thresholdAngle the minimum angle (in degrees),
+ * between the face normals of adjacent faces,
+ * that is required to render an edge. A value of 10 means
+ * an edge is only rendered if the angle is at least 10 degrees.
+ */
+
+THREE.EdgesHelper = function ( object, hex, thresholdAngle ) {
+
+ var color = ( hex !== undefined ) ? hex : 0xffffff;
+
+ THREE.LineSegments.call( this, new THREE.EdgesGeometry( object.geometry, thresholdAngle ), new THREE.LineBasicMaterial( { color: color } ) );
+
+ this.matrix = object.matrixWorld;
+ this.matrixAutoUpdate = false;
+
+};
+
+THREE.EdgesHelper.prototype = Object.create( THREE.LineSegments.prototype );
+THREE.EdgesHelper.prototype.constructor = THREE.EdgesHelper;
+
+// File:src/extras/helpers/FaceNormalsHelper.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+*/
+
+THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) {
+
+ // FaceNormalsHelper only supports THREE.Geometry
+
+ this.object = object;
+
+ this.size = ( size !== undefined ) ? size : 1;
+
+ var color = ( hex !== undefined ) ? hex : 0xffff00;
+
+ var width = ( linewidth !== undefined ) ? linewidth : 1;
+
+ //
+
+ var nNormals = 0;
+
+ var objGeometry = this.object.geometry;
+
+ if ( objGeometry instanceof THREE.Geometry ) {
+
+ nNormals = objGeometry.faces.length;
+
+ } else {
+
+ console.warn( 'THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.' );
+
+ }
+
+ //
+
+ var geometry = new THREE.BufferGeometry();
+
+ var positions = new THREE.Float32Attribute( nNormals * 2 * 3, 3 );
+
+ geometry.addAttribute( 'position', positions );
+
+ THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ) );
+
+ //
+
+ this.matrixAutoUpdate = false;
+ this.update();
+
+};
+
+THREE.FaceNormalsHelper.prototype = Object.create( THREE.LineSegments.prototype );
+THREE.FaceNormalsHelper.prototype.constructor = THREE.FaceNormalsHelper;
+
+THREE.FaceNormalsHelper.prototype.update = ( function () {
+
+ var v1 = new THREE.Vector3();
+ var v2 = new THREE.Vector3();
+ var normalMatrix = new THREE.Matrix3();
+
+ return function update() {
+
+ this.object.updateMatrixWorld( true );
+
+ normalMatrix.getNormalMatrix( this.object.matrixWorld );
+
+ var matrixWorld = this.object.matrixWorld;
+
+ var position = this.geometry.attributes.position;
+
+ //
+
+ var objGeometry = this.object.geometry;
+
+ var vertices = objGeometry.vertices;
+
+ var faces = objGeometry.faces;
+
+ var idx = 0;
+
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
+
+ var face = faces[ i ];
+
+ var normal = face.normal;
+
+ v1.copy( vertices[ face.a ] )
+ .add( vertices[ face.b ] )
+ .add( vertices[ face.c ] )
+ .divideScalar( 3 )
+ .applyMatrix4( matrixWorld );
+
+ v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );
+
+ position.setXYZ( idx, v1.x, v1.y, v1.z );
+
+ idx = idx + 1;
+
+ position.setXYZ( idx, v2.x, v2.y, v2.z );
+
+ idx = idx + 1;
+
+ }
+
+ position.needsUpdate = true;
+
+ return this;
+
+ }
+
+}() );
+
+// File:src/extras/helpers/GridHelper.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.GridHelper = function ( size, step ) {
+
+ var geometry = new THREE.Geometry();
+ var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
+
+ this.color1 = new THREE.Color( 0x444444 );
+ this.color2 = new THREE.Color( 0x888888 );
+
+ for ( var i = - size; i <= size; i += step ) {
+
+ geometry.vertices.push(
+ new THREE.Vector3( - size, 0, i ), new THREE.Vector3( size, 0, i ),
+ new THREE.Vector3( i, 0, - size ), new THREE.Vector3( i, 0, size )
+ );
+
+ var color = i === 0 ? this.color1 : this.color2;
+
+ geometry.colors.push( color, color, color, color );
+
+ }
+
+ THREE.LineSegments.call( this, geometry, material );
+
+};
+
+THREE.GridHelper.prototype = Object.create( THREE.LineSegments.prototype );
+THREE.GridHelper.prototype.constructor = THREE.GridHelper;
+
+THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) {
+
+ this.color1.set( colorCenterLine );
+ this.color2.set( colorGrid );
+
+ this.geometry.colorsNeedUpdate = true;
+
+};
+
+// File:src/extras/helpers/HemisphereLightHelper.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.HemisphereLightHelper = function ( light, sphereSize ) {
+
+ THREE.Object3D.call( this );
+
+ this.light = light;
+ this.light.updateMatrixWorld();
+
+ this.matrix = light.matrixWorld;
+ this.matrixAutoUpdate = false;
+
+ this.colors = [ new THREE.Color(), new THREE.Color() ];
+
+ var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 );
+ geometry.rotateX( - Math.PI / 2 );
+
+ for ( var i = 0, il = 8; i < il; i ++ ) {
+
+ geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ];
+
+ }
+
+ var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } );
+
+ this.lightSphere = new THREE.Mesh( geometry, material );
+ this.add( this.lightSphere );
+
+ this.update();
+
+};
+
+THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+THREE.HemisphereLightHelper.prototype.constructor = THREE.HemisphereLightHelper;
+
+THREE.HemisphereLightHelper.prototype.dispose = function () {
+
+ this.lightSphere.geometry.dispose();
+ this.lightSphere.material.dispose();
+
+};
+
+THREE.HemisphereLightHelper.prototype.update = function () {
+
+ var vector = new THREE.Vector3();
+
+ return function () {
+
+ this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity );
+ this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity );
+
+ this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() );
+ this.lightSphere.geometry.colorsNeedUpdate = true;
+
+ }
+
+}();
+
+// File:src/extras/helpers/PointLightHelper.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.PointLightHelper = function ( light, sphereSize ) {
+
+ this.light = light;
+ this.light.updateMatrixWorld();
+
+ var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 );
+ var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
+ material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+
+ THREE.Mesh.call( this, geometry, material );
+
+ this.matrix = this.light.matrixWorld;
+ this.matrixAutoUpdate = false;
+
+ /*
+ var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 );
+ var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );
+
+ this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );
+ this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );
+
+ var d = light.distance;
+
+ if ( d === 0.0 ) {
+
+ this.lightDistance.visible = false;
+
+ } else {
+
+ this.lightDistance.scale.set( d, d, d );
+
+ }
+
+ this.add( this.lightDistance );
+ */
+
+};
+
+THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype );
+THREE.PointLightHelper.prototype.constructor = THREE.PointLightHelper;
+
+THREE.PointLightHelper.prototype.dispose = function () {
+
+ this.geometry.dispose();
+ this.material.dispose();
+
+};
+
+THREE.PointLightHelper.prototype.update = function () {
+
+ this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+
+ /*
+ var d = this.light.distance;
+
+ if ( d === 0.0 ) {
+
+ this.lightDistance.visible = false;
+
+ } else {
+
+ this.lightDistance.visible = true;
+ this.lightDistance.scale.set( d, d, d );
+
+ }
+ */
+
+};
+
+// File:src/extras/helpers/SkeletonHelper.js
+
+/**
+ * @author Sean Griffin / http://twitter.com/sgrif
+ * @author Michael Guerrero / http://realitymeltdown.com
+ * @author mrdoob / http://mrdoob.com/
+ * @author ikerr / http://verold.com
+ */
+
+THREE.SkeletonHelper = function ( object ) {
+
+ this.bones = this.getBoneList( object );
+
+ var geometry = new THREE.Geometry();
+
+ for ( var i = 0; i < this.bones.length; i ++ ) {
+
+ var bone = this.bones[ i ];
+
+ if ( bone.parent instanceof THREE.Bone ) {
+
+ geometry.vertices.push( new THREE.Vector3() );
+ geometry.vertices.push( new THREE.Vector3() );
+ geometry.colors.push( new THREE.Color( 0, 0, 1 ) );
+ geometry.colors.push( new THREE.Color( 0, 1, 0 ) );
+
+ }
+
+ }
+
+ geometry.dynamic = true;
+
+ var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } );
+
+ THREE.LineSegments.call( this, geometry, material );
+
+ this.root = object;
+
+ this.matrix = object.matrixWorld;
+ this.matrixAutoUpdate = false;
+
+ this.update();
+
+};
+
+
+THREE.SkeletonHelper.prototype = Object.create( THREE.LineSegments.prototype );
+THREE.SkeletonHelper.prototype.constructor = THREE.SkeletonHelper;
+
+THREE.SkeletonHelper.prototype.getBoneList = function( object ) {
+
+ var boneList = [];
+
+ if ( object instanceof THREE.Bone ) {
+
+ boneList.push( object );
+
+ }
+
+ for ( var i = 0; i < object.children.length; i ++ ) {
+
+ boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) );
+
+ }
+
+ return boneList;
+
+};
+
+THREE.SkeletonHelper.prototype.update = function () {
+
+ var geometry = this.geometry;
+
+ var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld );
+
+ var boneMatrix = new THREE.Matrix4();
+
+ var j = 0;
+
+ for ( var i = 0; i < this.bones.length; i ++ ) {
+
+ var bone = this.bones[ i ];
+
+ if ( bone.parent instanceof THREE.Bone ) {
+
+ boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld );
+ geometry.vertices[ j ].setFromMatrixPosition( boneMatrix );
+
+ boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld );
+ geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix );
+
+ j += 2;
+
+ }
+
+ }
+
+ geometry.verticesNeedUpdate = true;
+
+ geometry.computeBoundingSphere();
+
+};
+
+// File:src/extras/helpers/SpotLightHelper.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+*/
+
+THREE.SpotLightHelper = function ( light ) {
+
+ THREE.Object3D.call( this );
+
+ this.light = light;
+ this.light.updateMatrixWorld();
+
+ this.matrix = light.matrixWorld;
+ this.matrixAutoUpdate = false;
+
+ var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true );
+
+ geometry.translate( 0, - 0.5, 0 );
+ geometry.rotateX( - Math.PI / 2 );
+
+ var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
+
+ this.cone = new THREE.Mesh( geometry, material );
+ this.add( this.cone );
+
+ this.update();
+
+};
+
+THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+THREE.SpotLightHelper.prototype.constructor = THREE.SpotLightHelper;
+
+THREE.SpotLightHelper.prototype.dispose = function () {
+
+ this.cone.geometry.dispose();
+ this.cone.material.dispose();
+
+};
+
+THREE.SpotLightHelper.prototype.update = function () {
+
+ var vector = new THREE.Vector3();
+ var vector2 = new THREE.Vector3();
+
+ return function () {
+
+ var coneLength = this.light.distance ? this.light.distance : 10000;
+ var coneWidth = coneLength * Math.tan( this.light.angle );
+
+ this.cone.scale.set( coneWidth, coneWidth, coneLength );
+
+ vector.setFromMatrixPosition( this.light.matrixWorld );
+ vector2.setFromMatrixPosition( this.light.target.matrixWorld );
+
+ this.cone.lookAt( vector2.sub( vector ) );
+
+ this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+
+ };
+
+}();
+
+// File:src/extras/helpers/VertexNormalsHelper.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+*/
+
+THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) {
+
+ this.object = object;
+
+ this.size = ( size !== undefined ) ? size : 1;
+
+ var color = ( hex !== undefined ) ? hex : 0xff0000;
+
+ var width = ( linewidth !== undefined ) ? linewidth : 1;
+
+ //
+
+ var nNormals = 0;
+
+ var objGeometry = this.object.geometry;
+
+ if ( objGeometry instanceof THREE.Geometry ) {
+
+ nNormals = objGeometry.faces.length * 3;
+
+ } else if ( objGeometry instanceof THREE.BufferGeometry ) {
+
+ nNormals = objGeometry.attributes.normal.count
+
+ }
+
+ //
+
+ var geometry = new THREE.BufferGeometry();
+
+ var positions = new THREE.Float32Attribute( nNormals * 2 * 3, 3 );
+
+ geometry.addAttribute( 'position', positions );
+
+ THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ) );
+
+ //
+
+ this.matrixAutoUpdate = false;
+
+ this.update();
+
+};
+
+THREE.VertexNormalsHelper.prototype = Object.create( THREE.LineSegments.prototype );
+THREE.VertexNormalsHelper.prototype.constructor = THREE.VertexNormalsHelper;
+
+THREE.VertexNormalsHelper.prototype.update = ( function () {
+
+ var v1 = new THREE.Vector3();
+ var v2 = new THREE.Vector3();
+ var normalMatrix = new THREE.Matrix3();
+
+ return function update() {
+
+ var keys = [ 'a', 'b', 'c' ];
+
+ this.object.updateMatrixWorld( true );
+
+ normalMatrix.getNormalMatrix( this.object.matrixWorld );
+
+ var matrixWorld = this.object.matrixWorld;
+
+ var position = this.geometry.attributes.position;
+
+ //
+
+ var objGeometry = this.object.geometry;
+
+ if ( objGeometry instanceof THREE.Geometry ) {
+
+ var vertices = objGeometry.vertices;
+
+ var faces = objGeometry.faces;
+
+ var idx = 0;
+
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
+
+ var face = faces[ i ];
+
+ for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
+
+ var vertex = vertices[ face[ keys[ j ] ] ];
+
+ var normal = face.vertexNormals[ j ];
+
+ v1.copy( vertex ).applyMatrix4( matrixWorld );
+
+ v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );
+
+ position.setXYZ( idx, v1.x, v1.y, v1.z );
+
+ idx = idx + 1;
+
+ position.setXYZ( idx, v2.x, v2.y, v2.z );
+
+ idx = idx + 1;
+
+ }
+
+ }
+
+ } else if ( objGeometry instanceof THREE.BufferGeometry ) {
+
+ var objPos = objGeometry.attributes.position;
+
+ var objNorm = objGeometry.attributes.normal;
+
+ var idx = 0;
+
+ // for simplicity, ignore index and drawcalls, and render every normal
+
+ for ( var j = 0, jl = objPos.count; j < jl; j ++ ) {
+
+ v1.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld );
+
+ v2.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) );
+
+ v2.applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );
+
+ position.setXYZ( idx, v1.x, v1.y, v1.z );
+
+ idx = idx + 1;
+
+ position.setXYZ( idx, v2.x, v2.y, v2.z );
+
+ idx = idx + 1;
+
+ }
+
+ }
+
+ position.needsUpdate = true;
+
+ return this;
+
+ }
+
+}() );
+
+// File:src/extras/helpers/WireframeHelper.js
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.WireframeHelper = function ( object, hex ) {
+
+ var color = ( hex !== undefined ) ? hex : 0xffffff;
+
+ THREE.LineSegments.call( this, new THREE.WireframeGeometry( object.geometry ), new THREE.LineBasicMaterial( { color: color } ) );
+
+ this.matrix = object.matrixWorld;
+ this.matrixAutoUpdate = false;
+
+};
+
+THREE.WireframeHelper.prototype = Object.create( THREE.LineSegments.prototype );
+THREE.WireframeHelper.prototype.constructor = THREE.WireframeHelper;
+
+// File:src/extras/objects/ImmediateRenderObject.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.ImmediateRenderObject = function ( material ) {
+
+ THREE.Object3D.call( this );
+
+ this.material = material;
+ this.render = function ( renderCallback ) {};
+
+};
+
+THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype );
+THREE.ImmediateRenderObject.prototype.constructor = THREE.ImmediateRenderObject;
+
+// File:src/extras/objects/MorphBlendMesh.js
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.MorphBlendMesh = function( geometry, material ) {
+
+ THREE.Mesh.call( this, geometry, material );
+
+ this.animationsMap = {};
+ this.animationsList = [];
+
+ // prepare default animation
+ // (all frames played together in 1 second)
+
+ var numFrames = this.geometry.morphTargets.length;
+
+ var name = "__default";
+
+ var startFrame = 0;
+ var endFrame = numFrames - 1;
+
+ var fps = numFrames / 1;
+
+ this.createAnimation( name, startFrame, endFrame, fps );
+ this.setAnimationWeight( name, 1 );
+
+};
+
+THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype );
+THREE.MorphBlendMesh.prototype.constructor = THREE.MorphBlendMesh;
+
+THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) {
+
+ var animation = {
+
+ start: start,
+ end: end,
+
+ length: end - start + 1,
+
+ fps: fps,
+ duration: ( end - start ) / fps,
+
+ lastFrame: 0,
+ currentFrame: 0,
+
+ active: false,
+
+ time: 0,
+ direction: 1,
+ weight: 1,
+
+ directionBackwards: false,
+ mirroredLoop: false
+
+ };
+
+ this.animationsMap[ name ] = animation;
+ this.animationsList.push( animation );
+
+};
+
+THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) {
+
+ var pattern = /([a-z]+)_?(\d+)/;
+
+ var firstAnimation, frameRanges = {};
+
+ var geometry = this.geometry;
+
+ for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
+
+ var morph = geometry.morphTargets[ i ];
+ var chunks = morph.name.match( pattern );
+
+ if ( chunks && chunks.length > 1 ) {
+
+ var name = chunks[ 1 ];
+
+ if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity };
+
+ var range = frameRanges[ name ];
+
+ if ( i < range.start ) range.start = i;
+ if ( i > range.end ) range.end = i;
+
+ if ( ! firstAnimation ) firstAnimation = name;
+
+ }
+
+ }
+
+ for ( var name in frameRanges ) {
+
+ var range = frameRanges[ name ];
+ this.createAnimation( name, range.start, range.end, fps );
+
+ }
+
+ this.firstAnimation = firstAnimation;
+
+};
+
+THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) {
+
+ var animation = this.animationsMap[ name ];
+
+ if ( animation ) {
+
+ animation.direction = 1;
+ animation.directionBackwards = false;
+
+ }
+
+};
+
+THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) {
+
+ var animation = this.animationsMap[ name ];
+
+ if ( animation ) {
+
+ animation.direction = - 1;
+ animation.directionBackwards = true;
+
+ }
+
+};
+
+THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) {
+
+ var animation = this.animationsMap[ name ];
+
+ if ( animation ) {
+
+ animation.fps = fps;
+ animation.duration = ( animation.end - animation.start ) / animation.fps;
+
+ }
+
+};
+
+THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) {
+
+ var animation = this.animationsMap[ name ];
+
+ if ( animation ) {
+
+ animation.duration = duration;
+ animation.fps = ( animation.end - animation.start ) / animation.duration;
+
+ }
+
+};
+
+THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) {
+
+ var animation = this.animationsMap[ name ];
+
+ if ( animation ) {
+
+ animation.weight = weight;
+
+ }
+
+};
+
+THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) {
+
+ var animation = this.animationsMap[ name ];
+
+ if ( animation ) {
+
+ animation.time = time;
+
+ }
+
+};
+
+THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) {
+
+ var time = 0;
+
+ var animation = this.animationsMap[ name ];
+
+ if ( animation ) {
+
+ time = animation.time;
+
+ }
+
+ return time;
+
+};
+
+THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) {
+
+ var duration = - 1;
+
+ var animation = this.animationsMap[ name ];
+
+ if ( animation ) {
+
+ duration = animation.duration;
+
+ }
+
+ return duration;
+
+};
+
+THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) {
+
+ var animation = this.animationsMap[ name ];
+
+ if ( animation ) {
+
+ animation.time = 0;
+ animation.active = true;
+
+ } else {
+
+ console.warn( "THREE.MorphBlendMesh: animation[" + name + "] undefined in .playAnimation()" );
+
+ }
+
+};
+
+THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) {
+
+ var animation = this.animationsMap[ name ];
+
+ if ( animation ) {
+
+ animation.active = false;
+
+ }
+
+};
+
+THREE.MorphBlendMesh.prototype.update = function ( delta ) {
+
+ for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) {
+
+ var animation = this.animationsList[ i ];
+
+ if ( ! animation.active ) continue;
+
+ var frameTime = animation.duration / animation.length;
+
+ animation.time += animation.direction * delta;
+
+ if ( animation.mirroredLoop ) {
+
+ if ( animation.time > animation.duration || animation.time < 0 ) {
+
+ animation.direction *= - 1;
+
+ if ( animation.time > animation.duration ) {
+
+ animation.time = animation.duration;
+ animation.directionBackwards = true;
+
+ }
+
+ if ( animation.time < 0 ) {
+
+ animation.time = 0;
+ animation.directionBackwards = false;
+
+ }
+
+ }
+
+ } else {
+
+ animation.time = animation.time % animation.duration;
+
+ if ( animation.time < 0 ) animation.time += animation.duration;
+
+ }
+
+ var keyframe = animation.start + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 );
+ var weight = animation.weight;
+
+ if ( keyframe !== animation.currentFrame ) {
+
+ this.morphTargetInfluences[ animation.lastFrame ] = 0;
+ this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight;
+
+ this.morphTargetInfluences[ keyframe ] = 0;
+
+ animation.lastFrame = animation.currentFrame;
+ animation.currentFrame = keyframe;
+
+ }
+
+ var mix = ( animation.time % frameTime ) / frameTime;
+
+ if ( animation.directionBackwards ) mix = 1 - mix;
+
+ if ( animation.currentFrame !== animation.lastFrame ) {
+
+ this.morphTargetInfluences[ animation.currentFrame ] = mix * weight;
+ this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight;
+
+ } else {
+
+ this.morphTargetInfluences[ animation.currentFrame ] = weight;
+
+ }
+
+ }
+
+};
+
+
+// Export the THREE object for **Node.js**, with
+// backwards-compatibility for the old `require()` API. If we're in
+// the browser, add `_` as a global object via a string identifier,
+// for Closure Compiler "advanced" mode.
+if (typeof exports !== 'undefined') {
+ if (typeof module !== 'undefined' && module.exports) {
+ exports = module.exports = THREE;
+ }
+ exports.THREE = THREE;
+} else {
+ this['THREE'] = THREE;
+}
+
+},{}],6:[function(_dereq_,module,exports){
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof _dereq_=="function"&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof _dereq_=="function"&&_dereq_;for(var o=0;o 0.0 && abs(vUV.x - 0.5) < .001) {',
+ // Don't render the divider, since it's rendered in HTML.
+ //'gl_FragColor = dividerColor;',
+ '} else if (a.x < 0.0 || a.x > 1.0 || a.y < 0.0 || a.y > 1.0) {',
+ 'gl_FragColor = backgroundColor;',
+ '} else {',
+ 'gl_FragColor = texture2D(texture, vec2(a.x * 0.5 + (vUV.x < 0.5 ? 0.0 : 0.5), a.y));',
+ '}',
+ '}'
+
+ ].join('\n')
+};
+
+module.exports = BarrelDistortionFragment;
+
+},{}],6:[function(_dereq_,module,exports){
+/**
+ * TODO(smus): Implement coefficient inversion.
+ */
+function Distortion(coefficients) {
+ this.coefficients = coefficients;
+}
+
+/**
+ * Calculates the inverse distortion for a radius.
+ *
+ * Allows to compute the original undistorted radius from a distorted one.
+ * See also getApproximateInverseDistortion() for a faster but potentially
+ * less accurate method.
+ *
+ * @param {Number} radius Distorted radius from the lens center in tan-angle units.
+ * @return {Number} The undistorted radius in tan-angle units.
+ */
+Distortion.prototype.distortInverse = function(radius) {
+ // Secant method.
+ var r0 = radius / 0.9;
+ var r1 = radius * 0.9;
+ var dr0 = radius - this.distort(r0);
+ while (Math.abs(r1 - r0) > 0.0001 /** 0.1mm */) {
+ var dr1 = radius - this.distort(r1);
+ var r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0));
+ r0 = r1;
+ r1 = r2;
+ dr0 = dr1;
+ }
+ return r1;
+}
+
+
+/**
+ * Distorts a radius by its distortion factor from the center of the lenses.
+ *
+ * @param {Number} radius Radius from the lens center in tan-angle units.
+ * @return {Number} The distorted radius in tan-angle units.
+ */
+Distortion.prototype.distort = function(radius) {
+ return radius * this.distortionFactor_(radius);
+}
+
+/**
+ * Returns the distortion factor of a point.
+ *
+ * @param {Number} radius Radius of the point from the lens center in tan-angle units.
+ * @return {Number} The distortion factor. Multiply by this factor to distort points.
+ */
+Distortion.prototype.distortionFactor_ = function(radius) {
+ var result = 1.0;
+ var rFactor = 1.0;
+ var rSquared = radius * radius;
+
+ for (var i = 0; i < this.coefficients.length; i++) {
+ var ki = this.coefficients[i];
+ rFactor *= rSquared;
+ result += ki * rFactor;
+ }
+
+ return result;
+}
+
+module.exports = Distortion;
+
+},{}],7:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * DPDB cache.
+ */
+var DPDB_CACHE = {
+ "format": 1,
+ "last_updated": "2016-01-26T23:11:18Z",
+ "devices": [
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "asus/*/Nexus 7/*" },
+ { "ua": "Nexus 7" }
+ ],
+ "dpi": [ 320.8, 323.0 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "asus/*/ASUS_Z00AD/*" },
+ { "ua": "ASUS_Z00AD" }
+ ],
+ "dpi": [ 403.0, 404.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "HTC/*/HTC6435LVW/*" },
+ { "ua": "HTC6435LVW" }
+ ],
+ "dpi": [ 449.7, 443.3 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "HTC/*/HTC One XL/*" },
+ { "ua": "HTC One XL" }
+ ],
+ "dpi": [ 315.3, 314.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "htc/*/Nexus 9/*" },
+ { "ua": "Nexus 9" }
+ ],
+ "dpi": 289.0,
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "HTC/*/HTC One M9/*" },
+ { "ua": "HTC One M9" }
+ ],
+ "dpi": [ 442.5, 443.3 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "HTC/*/HTC One_M8/*" },
+ { "ua": "HTC One_M8" }
+ ],
+ "dpi": [ 449.7, 447.4 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "HTC/*/HTC One/*" },
+ { "ua": "HTC One" }
+ ],
+ "dpi": 472.8,
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Huawei/*/Nexus 6P/*" },
+ { "ua": "Nexus 6P" }
+ ],
+ "dpi": [ 515.1, 518.0 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/Nexus 5X/*" },
+ { "ua": "Nexus 5X" }
+ ],
+ "dpi": [ 422.0, 419.9 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/LGMS345/*" },
+ { "ua": "LGMS345" }
+ ],
+ "dpi": [ 221.7, 219.1 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/LG-D800/*" },
+ { "ua": "LG-D800" }
+ ],
+ "dpi": [ 422.0, 424.1 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/LG-D850/*" },
+ { "ua": "LG-D850" }
+ ],
+ "dpi": [ 537.9, 541.9 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/VS985 4G/*" },
+ { "ua": "VS985 4G" }
+ ],
+ "dpi": [ 537.9, 535.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/Nexus 5/*" },
+ { "ua": "Nexus 5 B" }
+ ],
+ "dpi": [ 442.4, 444.8 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/Nexus 4/*" },
+ { "ua": "Nexus 4" }
+ ],
+ "dpi": [ 319.8, 318.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/LG-P769/*" },
+ { "ua": "LG-P769" }
+ ],
+ "dpi": [ 240.6, 247.5 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/LGMS323/*" },
+ { "ua": "LGMS323" }
+ ],
+ "dpi": [ 206.6, 204.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "LGE/*/LGLS996/*" },
+ { "ua": "LGLS996" }
+ ],
+ "dpi": [ 403.4, 401.5 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Micromax/*/4560MMX/*" },
+ { "ua": "4560MMX" }
+ ],
+ "dpi": [ 240.0, 219.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Micromax/*/A250/*" },
+ { "ua": "Micromax A250" }
+ ],
+ "dpi": [ 480.0, 446.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Micromax/*/Micromax AQ4501/*" },
+ { "ua": "Micromax AQ4501" }
+ ],
+ "dpi": 240.0,
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/DROID RAZR/*" },
+ { "ua": "DROID RAZR" }
+ ],
+ "dpi": [ 368.1, 256.7 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT830C/*" },
+ { "ua": "XT830C" }
+ ],
+ "dpi": [ 254.0, 255.9 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1021/*" },
+ { "ua": "XT1021" }
+ ],
+ "dpi": [ 254.0, 256.7 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1023/*" },
+ { "ua": "XT1023" }
+ ],
+ "dpi": [ 254.0, 256.7 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1028/*" },
+ { "ua": "XT1028" }
+ ],
+ "dpi": [ 326.6, 327.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1034/*" },
+ { "ua": "XT1034" }
+ ],
+ "dpi": [ 326.6, 328.4 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1053/*" },
+ { "ua": "XT1053" }
+ ],
+ "dpi": [ 315.3, 316.1 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1562/*" },
+ { "ua": "XT1562" }
+ ],
+ "dpi": [ 403.4, 402.7 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/Nexus 6/*" },
+ { "ua": "Nexus 6 B" }
+ ],
+ "dpi": [ 494.3, 489.7 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1063/*" },
+ { "ua": "XT1063" }
+ ],
+ "dpi": [ 295.0, 296.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1064/*" },
+ { "ua": "XT1064" }
+ ],
+ "dpi": [ 295.0, 295.6 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1092/*" },
+ { "ua": "XT1092" }
+ ],
+ "dpi": [ 422.0, 424.1 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "motorola/*/XT1095/*" },
+ { "ua": "XT1095" }
+ ],
+ "dpi": [ 422.0, 423.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "OnePlus/*/A0001/*" },
+ { "ua": "A0001" }
+ ],
+ "dpi": [ 403.4, 401.0 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "OnePlus/*/ONE E1005/*" },
+ { "ua": "ONE E1005" }
+ ],
+ "dpi": [ 442.4, 441.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "OnePlus/*/ONE A2005/*" },
+ { "ua": "ONE A2005" }
+ ],
+ "dpi": [ 391.9, 405.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "OPPO/*/X909/*" },
+ { "ua": "X909" }
+ ],
+ "dpi": [ 442.4, 444.1 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-I9082/*" },
+ { "ua": "GT-I9082" }
+ ],
+ "dpi": [ 184.7, 185.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G360P/*" },
+ { "ua": "SM-G360P" }
+ ],
+ "dpi": [ 196.7, 205.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/Nexus S/*" },
+ { "ua": "Nexus S" }
+ ],
+ "dpi": [ 234.5, 229.8 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-I9300/*" },
+ { "ua": "GT-I9300" }
+ ],
+ "dpi": [ 304.8, 303.9 ],
+ "bw": 5,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-T230NU/*" },
+ { "ua": "SM-T230NU" }
+ ],
+ "dpi": 216.0,
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SGH-T399/*" },
+ { "ua": "SGH-T399" }
+ ],
+ "dpi": [ 217.7, 231.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-N9005/*" },
+ { "ua": "SM-N9005" }
+ ],
+ "dpi": [ 386.4, 387.0 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SAMSUNG-SM-N900A/*" },
+ { "ua": "SAMSUNG-SM-N900A" }
+ ],
+ "dpi": [ 386.4, 387.7 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-I9500/*" },
+ { "ua": "GT-I9500" }
+ ],
+ "dpi": [ 442.5, 443.3 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-I9505/*" },
+ { "ua": "GT-I9505" }
+ ],
+ "dpi": 439.4,
+ "bw": 4,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G900F/*" },
+ { "ua": "SM-G900F" }
+ ],
+ "dpi": [ 415.6, 431.6 ],
+ "bw": 5,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G900M/*" },
+ { "ua": "SM-G900M" }
+ ],
+ "dpi": [ 415.6, 431.6 ],
+ "bw": 5,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G800F/*" },
+ { "ua": "SM-G800F" }
+ ],
+ "dpi": 326.8,
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G906S/*" },
+ { "ua": "SM-G906S" }
+ ],
+ "dpi": [ 562.7, 572.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-I9300/*" },
+ { "ua": "GT-I9300" }
+ ],
+ "dpi": [ 306.7, 304.8 ],
+ "bw": 5,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-T535/*" },
+ { "ua": "SM-T535" }
+ ],
+ "dpi": [ 142.6, 136.4 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-N920C/*" },
+ { "ua": "SM-N920C" }
+ ],
+ "dpi": [ 515.1, 518.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-I9300I/*" },
+ { "ua": "GT-I9300I" }
+ ],
+ "dpi": [ 304.8, 305.8 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-I9195/*" },
+ { "ua": "GT-I9195" }
+ ],
+ "dpi": [ 249.4, 256.7 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SPH-L520/*" },
+ { "ua": "SPH-L520" }
+ ],
+ "dpi": [ 249.4, 255.9 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SAMSUNG-SGH-I717/*" },
+ { "ua": "SAMSUNG-SGH-I717" }
+ ],
+ "dpi": 285.8,
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SPH-D710/*" },
+ { "ua": "SPH-D710" }
+ ],
+ "dpi": [ 217.7, 204.2 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/GT-N7100/*" },
+ { "ua": "GT-N7100" }
+ ],
+ "dpi": 265.1,
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SCH-I605/*" },
+ { "ua": "SCH-I605" }
+ ],
+ "dpi": 265.1,
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/Galaxy Nexus/*" },
+ { "ua": "Galaxy Nexus" }
+ ],
+ "dpi": [ 315.3, 314.2 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-N910H/*" },
+ { "ua": "SM-N910H" }
+ ],
+ "dpi": [ 515.1, 518.0 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-N910C/*" },
+ { "ua": "SM-N910C" }
+ ],
+ "dpi": [ 515.2, 520.2 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G130M/*" },
+ { "ua": "SM-G130M" }
+ ],
+ "dpi": [ 165.9, 164.8 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G928I/*" },
+ { "ua": "SM-G928I" }
+ ],
+ "dpi": [ 515.1, 518.4 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G920F/*" },
+ { "ua": "SM-G920F" }
+ ],
+ "dpi": 580.6,
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G920P/*" },
+ { "ua": "SM-G920P" }
+ ],
+ "dpi": [ 522.5, 577.0 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G925F/*" },
+ { "ua": "SM-G925F" }
+ ],
+ "dpi": 580.6,
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "samsung/*/SM-G925V/*" },
+ { "ua": "SM-G925V" }
+ ],
+ "dpi": [ 522.5, 576.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Sony/*/C6903/*" },
+ { "ua": "C6903" }
+ ],
+ "dpi": [ 442.5, 443.3 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Sony/*/D6653/*" },
+ { "ua": "D6653" }
+ ],
+ "dpi": [ 428.6, 427.6 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Sony/*/E6653/*" },
+ { "ua": "E6653" }
+ ],
+ "dpi": [ 428.6, 425.7 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Sony/*/E6853/*" },
+ { "ua": "E6853" }
+ ],
+ "dpi": [ 403.4, 401.9 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "Sony/*/SGP321/*" },
+ { "ua": "SGP321" }
+ ],
+ "dpi": [ 224.7, 224.1 ],
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "TCT/*/ALCATEL ONE TOUCH Fierce/*" },
+ { "ua": "ALCATEL ONE TOUCH Fierce" }
+ ],
+ "dpi": [ 240.0, 247.5 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "THL/*/thl 5000/*" },
+ { "ua": "thl 5000" }
+ ],
+ "dpi": [ 480.0, 443.3 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "android",
+ "rules": [
+ { "mdmh": "ZTE/*/ZTE Blade L2/*" },
+ { "ua": "ZTE Blade L2" }
+ ],
+ "dpi": 240.0,
+ "bw": 3,
+ "ac": 500
+ },
+ {
+ "type": "ios",
+ "rules": [ { "res": [ 640, 960 ] } ],
+ "dpi": [ 325.1, 328.4 ],
+ "bw": 4,
+ "ac": 1000
+ },
+ {
+ "type": "ios",
+ "rules": [ { "res": [ 640, 1136 ] } ],
+ "dpi": [ 317.1, 320.2 ],
+ "bw": 3,
+ "ac": 1000
+ },
+ {
+ "type": "ios",
+ "rules": [ { "res": [ 750, 1334 ] } ],
+ "dpi": 326.4,
+ "bw": 4,
+ "ac": 1000
+ },
+ {
+ "type": "ios",
+ "rules": [ { "res": [ 1242, 2208 ] } ],
+ "dpi": [ 453.6, 458.4 ],
+ "bw": 4,
+ "ac": 1000
+ },
+ {
+ "type": "ios",
+ "rules": [ { "res": [ 1125, 2001 ] } ],
+ "dpi": [ 410.9, 415.4 ],
+ "bw": 4,
+ "ac": 1000
+ }
+]};
+
+module.exports = DPDB_CACHE;
+
+
+},{}],8:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Offline cache of the DPDB, to be used until we load the online one (and
+// as a fallback in case we can't load the online one).
+var DPDB_CACHE = _dereq_('./dpdb-cache.js');
+var Util = _dereq_('./util.js');
+
+// Online DPDB URL.
+var ONLINE_DPDB_URL = 'https://storage.googleapis.com/cardboard-dpdb/dpdb.json';
+
+/**
+ * Calculates device parameters based on the DPDB (Device Parameter Database).
+ * Initially, uses the cached DPDB values.
+ *
+ * If fetchOnline == true, then this object tries to fetch the online version
+ * of the DPDB and updates the device info if a better match is found.
+ * Calls the onDeviceParamsUpdated callback when there is an update to the
+ * device information.
+ */
+function Dpdb(fetchOnline, onDeviceParamsUpdated) {
+ // Start with the offline DPDB cache while we are loading the real one.
+ this.dpdb = DPDB_CACHE;
+
+ // Calculate device params based on the offline version of the DPDB.
+ this.recalculateDeviceParams_();
+
+ // XHR to fetch online DPDB file, if requested.
+ if (fetchOnline) {
+ // Set the callback.
+ this.onDeviceParamsUpdated = onDeviceParamsUpdated;
+
+ console.log('Fetching DPDB...');
+ var xhr = new XMLHttpRequest();
+ var obj = this;
+ xhr.open('GET', ONLINE_DPDB_URL, true);
+ xhr.addEventListener('load', function() {
+ obj.loading = false;
+ if (xhr.status >= 200 && xhr.status <= 299) {
+ // Success.
+ console.log('Successfully loaded online DPDB.');
+ obj.dpdb = JSON.parse(xhr.response);
+ obj.recalculateDeviceParams_();
+ } else {
+ // Error loading the DPDB.
+ console.error('Error loading online DPDB!');
+ }
+ });
+ xhr.send();
+ }
+}
+
+// Returns the current device parameters.
+Dpdb.prototype.getDeviceParams = function() {
+ return this.deviceParams;
+};
+
+// Recalculates this device's parameters based on the DPDB.
+Dpdb.prototype.recalculateDeviceParams_ = function() {
+ console.log('Recalculating device params.');
+ var newDeviceParams = this.calcDeviceParams_();
+ console.log('New device parameters:');
+ console.log(newDeviceParams);
+ if (newDeviceParams) {
+ this.deviceParams = newDeviceParams;
+ // Invoke callback, if it is set.
+ if (this.onDeviceParamsUpdated) {
+ this.onDeviceParamsUpdated(this.deviceParams);
+ }
+ } else {
+ console.warn('Failed to recalculate device parameters.');
+ }
+};
+
+// Returns a DeviceParams object that represents the best guess as to this
+// device's parameters. Can return null if the device does not match any
+// known devices.
+Dpdb.prototype.calcDeviceParams_ = function() {
+ var db = this.dpdb; // shorthand
+ if (!db) {
+ console.error('DPDB not available.');
+ return null;
+ }
+ if (db.format != 1) {
+ console.error('DPDB has unexpected format version.');
+ return null;
+ }
+ if (!db.devices || !db.devices.length) {
+ console.error('DPDB does not have a devices section.');
+ return null;
+ }
+
+ // Get the actual user agent and screen dimensions in pixels.
+ var userAgent = navigator.userAgent || navigator.vendor || window.opera;
+ var width = Util.getScreenWidth();
+ var height = Util.getScreenHeight();
+ console.log('User agent: ' + userAgent);
+ console.log('Pixel width: ' + width);
+ console.log('Pixel height: ' + height);
+
+ if (!db.devices) {
+ console.error('DPDB has no devices section.');
+ return null;
+ }
+
+ for (var i = 0; i < db.devices.length; i++) {
+ var device = db.devices[i];
+ if (!device.rules) {
+ console.warn('Device[' + i + '] has no rules section.');
+ continue;
+ }
+
+ if (device.type != 'ios' && device.type != 'android') {
+ console.warn('Device[' + i + '] has invalid type.');
+ continue;
+ }
+
+ // See if this device is of the appropriate type.
+ if (Util.isIOS() != (device.type == 'ios')) continue;
+
+ // See if this device matches any of the rules:
+ var matched = false;
+ for (var j = 0; j < device.rules.length; j++) {
+ var rule = device.rules[j];
+ if (this.matchRule_(rule, userAgent, width, height)) {
+ console.log('Rule matched:');
+ console.log(rule);
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) continue;
+
+ // device.dpi might be an array of [ xdpi, ydpi] or just a scalar.
+ var xdpi = device.dpi[0] || device.dpi;
+ var ydpi = device.dpi[1] || device.dpi;
+
+ return new DeviceParams({ xdpi: xdpi, ydpi: ydpi, bevelMm: device.bw });
+ }
+
+ console.warn('No DPDB device match.');
+ return null;
+};
+
+Dpdb.prototype.matchRule_ = function(rule, ua, screenWidth, screenHeight) {
+ // We can only match 'ua' and 'res' rules, not other types like 'mdmh'
+ // (which are meant for native platforms).
+ if (!rule.ua && !rule.res) return false;
+
+ // If our user agent string doesn't contain the indicated user agent string,
+ // the match fails.
+ if (rule.ua && ua.indexOf(rule.ua) < 0) return false;
+
+ // If the rule specifies screen dimensions that don't correspond to ours,
+ // the match fails.
+ if (rule.res) {
+ if (!rule.res[0] || !rule.res[1]) return false;
+ var resX = rule.res[0];
+ var resY = rule.res[1];
+ // Compare min and max so as to make the order not matter, i.e., it should
+ // be true that 640x480 == 480x640.
+ if (Math.min(screenWidth, screenHeight) != Math.min(resX, resY) ||
+ (Math.max(screenWidth, screenHeight) != Math.max(resX, resY))) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+function DeviceParams(params) {
+ this.xdpi = params.xdpi;
+ this.ydpi = params.ydpi;
+ this.bevelMm = params.bevelMm;
+}
+
+module.exports = Dpdb;
+
+},{"./dpdb-cache.js":7,"./util.js":13}],9:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+function Emitter() {
+ this.callbacks = {};
+}
+
+Emitter.prototype.emit = function(eventName) {
+ var callbacks = this.callbacks[eventName];
+ if (!callbacks) {
+ //console.log('No valid callback specified.');
+ return;
+ }
+ var args = [].slice.call(arguments);
+ // Eliminate the first param (the callback).
+ args.shift();
+ for (var i = 0; i < callbacks.length; i++) {
+ callbacks[i].apply(this, args);
+ }
+};
+
+Emitter.prototype.on = function(eventName, callback) {
+ if (eventName in this.callbacks) {
+ this.callbacks[eventName].push(callback);
+ } else {
+ this.callbacks[eventName] = [callback];
+ }
+};
+
+module.exports = Emitter;
+
+},{}],10:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var WebVRManager = _dereq_('./webvr-manager.js');
+
+window.WebVRConfig = window.WebVRConfig || {};
+window.WebVRManager = WebVRManager;
+
+},{"./webvr-manager.js":16}],11:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var Modes = {
+ UNKNOWN: 0,
+ // Not fullscreen, just tracking.
+ NORMAL: 1,
+ // Magic window immersive mode.
+ MAGIC_WINDOW: 2,
+ // Full screen split screen VR mode.
+ VR: 3,
+};
+
+module.exports = Modes;
+
+},{}],12:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var Util = _dereq_('./util.js');
+
+function RotateInstructions() {
+ this.loadIcon_();
+
+ var overlay = document.createElement('div');
+ var s = overlay.style;
+ s.position = 'fixed';
+ s.top = 0;
+ s.right = 0;
+ s.bottom = 0;
+ s.left = 0;
+ s.backgroundColor = 'gray';
+ s.fontFamily = 'sans-serif';
+
+ var img = document.createElement('img');
+ img.src = this.icon;
+ var s = img.style;
+ s.marginLeft = '25%';
+ s.marginTop = '25%';
+ s.width = '50%';
+ overlay.appendChild(img);
+
+ var text = document.createElement('div');
+ var s = text.style;
+ s.textAlign = 'center';
+ s.fontSize = '16px';
+ s.lineHeight = '24px';
+ s.margin = '24px 25%';
+ s.width = '50%';
+ text.innerHTML = 'Place your phone into your Cardboard viewer.';
+ overlay.appendChild(text);
+
+ var snackbar = document.createElement('div');
+ var s = snackbar.style;
+ s.backgroundColor = '#CFD8DC';
+ s.position = 'fixed';
+ s.bottom = 0;
+ s.width = '100%';
+ s.height = '48px';
+ s.padding = '14px 24px';
+ s.boxSizing = 'border-box';
+ s.color = '#656A6B';
+ overlay.appendChild(snackbar);
+
+ var snackbarText = document.createElement('div');
+ snackbarText.style.float = 'left';
+ snackbarText.innerHTML = 'No Cardboard viewer?';
+
+ var snackbarButton = document.createElement('a');
+ snackbarButton.href = 'https://www.google.com/get/cardboard/get-cardboard/';
+ snackbarButton.innerHTML = 'get one';
+ snackbarButton.target = '_blank';
+ var s = snackbarButton.style;
+ s.float = 'right';
+ s.fontWeight = 600;
+ s.textTransform = 'uppercase';
+ s.borderLeft = '1px solid gray';
+ s.paddingLeft = '24px';
+ s.textDecoration = 'none';
+ s.color = '#656A6B';
+
+ snackbar.appendChild(snackbarText);
+ snackbar.appendChild(snackbarButton);
+
+ this.overlay = overlay;
+ this.text = text;
+ document.body.appendChild(overlay);
+
+ this.hide();
+}
+
+RotateInstructions.prototype.show = function() {
+ this.overlay.style.display = 'block';
+
+ var img = this.overlay.querySelector('img');
+ var s = img.style;
+
+ if (Util.isLandscapeMode()) {
+ s.width = '20%';
+ s.marginLeft = '40%';
+ s.marginTop = '3%';
+ } else {
+ s.width = '50%';
+ s.marginLeft = '25%';
+ s.marginTop = '25%';
+ }
+};
+
+RotateInstructions.prototype.hide = function() {
+ this.overlay.style.display = 'none';
+};
+
+RotateInstructions.prototype.showTemporarily = function(ms) {
+ this.show();
+ this.timer = setTimeout(this.hide.bind(this), ms);
+};
+
+RotateInstructions.prototype.disableShowTemporarily = function() {
+ clearTimeout(this.timer);
+};
+
+RotateInstructions.prototype.loadIcon_ = function() {
+ // Encoded asset_src/rotate-instructions.svg
+ this.icon = Util.base64('image/svg+xml', 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjE5OHB4IiBoZWlnaHQ9IjI0MHB4IiB2aWV3Qm94PSIwIDAgMTk4IDI0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpza2V0Y2g9Imh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaC9ucyI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDMuMy4zICgxMjA4MSkgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+dHJhbnNpdGlvbjwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPjwvZGVmcz4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHNrZXRjaDp0eXBlPSJNU1BhZ2UiPgogICAgICAgIDxnIGlkPSJ0cmFuc2l0aW9uIiBza2V0Y2g6dHlwZT0iTVNBcnRib2FyZEdyb3VwIj4KICAgICAgICAgICAgPGcgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTQtKy1JbXBvcnRlZC1MYXllcnMtQ29weS0rLUltcG9ydGVkLUxheWVycy1Db3B5LTItQ29weSIgc2tldGNoOnR5cGU9Ik1TTGF5ZXJHcm91cCI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHktNCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEwNy4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjYyNSwyLjUyNyBDMTQ5LjYyNSwyLjUyNyAxNTUuODA1LDYuMDk2IDE1Ni4zNjIsNi40MTggTDE1Ni4zNjIsNy4zMDQgQzE1Ni4zNjIsNy40ODEgMTU2LjM3NSw3LjY2NCAxNTYuNCw3Ljg1MyBDMTU2LjQxLDcuOTM0IDE1Ni40Miw4LjAxNSAxNTYuNDI3LDguMDk1IEMxNTYuNTY3LDkuNTEgMTU3LjQwMSwxMS4wOTMgMTU4LjUzMiwxMi4wOTQgTDE2NC4yNTIsMTcuMTU2IEwxNjQuMzMzLDE3LjA2NiBDMTY0LjMzMywxNy4wNjYgMTY4LjcxNSwxNC41MzYgMTY5LjU2OCwxNC4wNDIgQzE3MS4wMjUsMTQuODgzIDE5NS41MzgsMjkuMDM1IDE5NS41MzgsMjkuMDM1IEwxOTUuNTM4LDgzLjAzNiBDMTk1LjUzOCw4My44MDcgMTk1LjE1Miw4NC4yNTMgMTk0LjU5LDg0LjI1MyBDMTk0LjM1Nyw4NC4yNTMgMTk0LjA5NSw4NC4xNzcgMTkzLjgxOCw4NC4wMTcgTDE2OS44NTEsNzAuMTc5IEwxNjkuODM3LDcwLjIwMyBMMTQyLjUxNSw4NS45NzggTDE0MS42NjUsODQuNjU1IEMxMzYuOTM0LDgzLjEyNiAxMzEuOTE3LDgxLjkxNSAxMjYuNzE0LDgxLjA0NSBDMTI2LjcwOSw4MS4wNiAxMjYuNzA3LDgxLjA2OSAxMjYuNzA3LDgxLjA2OSBMMTIxLjY0LDk4LjAzIEwxMTMuNzQ5LDEwMi41ODYgTDExMy43MTIsMTAyLjUyMyBMMTEzLjcxMiwxMzAuMTEzIEMxMTMuNzEyLDEzMC44ODUgMTEzLjMyNiwxMzEuMzMgMTEyLjc2NCwxMzEuMzMgQzExMi41MzIsMTMxLjMzIDExMi4yNjksMTMxLjI1NCAxMTEuOTkyLDEzMS4wOTQgTDY5LjUxOSwxMDYuNTcyIEM2OC41NjksMTA2LjAyMyA2Ny43OTksMTA0LjY5NSA2Ny43OTksMTAzLjYwNSBMNjcuNzk5LDEwMi41NyBMNjcuNzc4LDEwMi42MTcgQzY3LjI3LDEwMi4zOTMgNjYuNjQ4LDEwMi4yNDkgNjUuOTYyLDEwMi4yMTggQzY1Ljg3NSwxMDIuMjE0IDY1Ljc4OCwxMDIuMjEyIDY1LjcwMSwxMDIuMjEyIEM2NS42MDYsMTAyLjIxMiA2NS41MTEsMTAyLjIxNSA2NS40MTYsMTAyLjIxOSBDNjUuMTk1LDEwMi4yMjkgNjQuOTc0LDEwMi4yMzUgNjQuNzU0LDEwMi4yMzUgQzY0LjMzMSwxMDIuMjM1IDYzLjkxMSwxMDIuMjE2IDYzLjQ5OCwxMDIuMTc4IEM2MS44NDMsMTAyLjAyNSA2MC4yOTgsMTAxLjU3OCA1OS4wOTQsMTAwLjg4MiBMMTIuNTE4LDczLjk5MiBMMTIuNTIzLDc0LjAwNCBMMi4yNDUsNTUuMjU0IEMxLjI0NCw1My40MjcgMi4wMDQsNTEuMDM4IDMuOTQzLDQ5LjkxOCBMNTkuOTU0LDE3LjU3MyBDNjAuNjI2LDE3LjE4NSA2MS4zNSwxNy4wMDEgNjIuMDUzLDE3LjAwMSBDNjMuMzc5LDE3LjAwMSA2NC42MjUsMTcuNjYgNjUuMjgsMTguODU0IEw2NS4yODUsMTguODUxIEw2NS41MTIsMTkuMjY0IEw2NS41MDYsMTkuMjY4IEM2NS45MDksMjAuMDAzIDY2LjQwNSwyMC42OCA2Ni45ODMsMjEuMjg2IEw2Ny4yNiwyMS41NTYgQzY5LjE3NCwyMy40MDYgNzEuNzI4LDI0LjM1NyA3NC4zNzMsMjQuMzU3IEM3Ni4zMjIsMjQuMzU3IDc4LjMyMSwyMy44NCA4MC4xNDgsMjIuNzg1IEM4MC4xNjEsMjIuNzg1IDg3LjQ2NywxOC41NjYgODcuNDY3LDE4LjU2NiBDODguMTM5LDE4LjE3OCA4OC44NjMsMTcuOTk0IDg5LjU2NiwxNy45OTQgQzkwLjg5MiwxNy45OTQgOTIuMTM4LDE4LjY1MiA5Mi43OTIsMTkuODQ3IEw5Ni4wNDIsMjUuNzc1IEw5Ni4wNjQsMjUuNzU3IEwxMDIuODQ5LDI5LjY3NCBMMTAyLjc0NCwyOS40OTIgTDE0OS42MjUsMi41MjcgTTE0OS42MjUsMC44OTIgQzE0OS4zNDMsMC44OTIgMTQ5LjA2MiwwLjk2NSAxNDguODEsMS4xMSBMMTAyLjY0MSwyNy42NjYgTDk3LjIzMSwyNC41NDIgTDk0LjIyNiwxOS4wNjEgQzkzLjMxMywxNy4zOTQgOTEuNTI3LDE2LjM1OSA4OS41NjYsMTYuMzU4IEM4OC41NTUsMTYuMzU4IDg3LjU0NiwxNi42MzIgODYuNjQ5LDE3LjE1IEM4My44NzgsMTguNzUgNzkuNjg3LDIxLjE2OSA3OS4zNzQsMjEuMzQ1IEM3OS4zNTksMjEuMzUzIDc5LjM0NSwyMS4zNjEgNzkuMzMsMjEuMzY5IEM3Ny43OTgsMjIuMjU0IDc2LjA4NCwyMi43MjIgNzQuMzczLDIyLjcyMiBDNzIuMDgxLDIyLjcyMiA2OS45NTksMjEuODkgNjguMzk3LDIwLjM4IEw2OC4xNDUsMjAuMTM1IEM2Ny43MDYsMTkuNjcyIDY3LjMyMywxOS4xNTYgNjcuMDA2LDE4LjYwMSBDNjYuOTg4LDE4LjU1OSA2Ni45NjgsMTguNTE5IDY2Ljk0NiwxOC40NzkgTDY2LjcxOSwxOC4wNjUgQzY2LjY5LDE4LjAxMiA2Ni42NTgsMTcuOTYgNjYuNjI0LDE3LjkxMSBDNjUuNjg2LDE2LjMzNyA2My45NTEsMTUuMzY2IDYyLjA1MywxNS4zNjYgQzYxLjA0MiwxNS4zNjYgNjAuMDMzLDE1LjY0IDU5LjEzNiwxNi4xNTggTDMuMTI1LDQ4LjUwMiBDMC40MjYsNTAuMDYxIC0wLjYxMyw1My40NDIgMC44MTEsNTYuMDQgTDExLjA4OSw3NC43OSBDMTEuMjY2LDc1LjExMyAxMS41MzcsNzUuMzUzIDExLjg1LDc1LjQ5NCBMNTguMjc2LDEwMi4yOTggQzU5LjY3OSwxMDMuMTA4IDYxLjQzMywxMDMuNjMgNjMuMzQ4LDEwMy44MDYgQzYzLjgxMiwxMDMuODQ4IDY0LjI4NSwxMDMuODcgNjQuNzU0LDEwMy44NyBDNjUsMTAzLjg3IDY1LjI0OSwxMDMuODY0IDY1LjQ5NCwxMDMuODUyIEM2NS41NjMsMTAzLjg0OSA2NS42MzIsMTAzLjg0NyA2NS43MDEsMTAzLjg0NyBDNjUuNzY0LDEwMy44NDcgNjUuODI4LDEwMy44NDkgNjUuODksMTAzLjg1MiBDNjUuOTg2LDEwMy44NTYgNjYuMDgsMTAzLjg2MyA2Ni4xNzMsMTAzLjg3NCBDNjYuMjgyLDEwNS40NjcgNjcuMzMyLDEwNy4xOTcgNjguNzAyLDEwNy45ODggTDExMS4xNzQsMTMyLjUxIEMxMTEuNjk4LDEzMi44MTIgMTEyLjIzMiwxMzIuOTY1IDExMi43NjQsMTMyLjk2NSBDMTE0LjI2MSwxMzIuOTY1IDExNS4zNDcsMTMxLjc2NSAxMTUuMzQ3LDEzMC4xMTMgTDExNS4zNDcsMTAzLjU1MSBMMTIyLjQ1OCw5OS40NDYgQzEyMi44MTksOTkuMjM3IDEyMy4wODcsOTguODk4IDEyMy4yMDcsOTguNDk4IEwxMjcuODY1LDgyLjkwNSBDMTMyLjI3OSw4My43MDIgMTM2LjU1Nyw4NC43NTMgMTQwLjYwNyw4Ni4wMzMgTDE0MS4xNCw4Ni44NjIgQzE0MS40NTEsODcuMzQ2IDE0MS45NzcsODcuNjEzIDE0Mi41MTYsODcuNjEzIEMxNDIuNzk0LDg3LjYxMyAxNDMuMDc2LDg3LjU0MiAxNDMuMzMzLDg3LjM5MyBMMTY5Ljg2NSw3Mi4wNzYgTDE5Myw4NS40MzMgQzE5My41MjMsODUuNzM1IDE5NC4wNTgsODUuODg4IDE5NC41OSw4NS44ODggQzE5Ni4wODcsODUuODg4IDE5Ny4xNzMsODQuNjg5IDE5Ny4xNzMsODMuMDM2IEwxOTcuMTczLDI5LjAzNSBDMTk3LjE3MywyOC40NTEgMTk2Ljg2MSwyNy45MTEgMTk2LjM1NSwyNy42MTkgQzE5Ni4zNTUsMjcuNjE5IDE3MS44NDMsMTMuNDY3IDE3MC4zODUsMTIuNjI2IEMxNzAuMTMyLDEyLjQ4IDE2OS44NSwxMi40MDcgMTY5LjU2OCwxMi40MDcgQzE2OS4yODUsMTIuNDA3IDE2OS4wMDIsMTIuNDgxIDE2OC43NDksMTIuNjI3IEMxNjguMTQzLDEyLjk3OCAxNjUuNzU2LDE0LjM1NyAxNjQuNDI0LDE1LjEyNSBMMTU5LjYxNSwxMC44NyBDMTU4Ljc5NiwxMC4xNDUgMTU4LjE1NCw4LjkzNyAxNTguMDU0LDcuOTM0IEMxNTguMDQ1LDcuODM3IDE1OC4wMzQsNy43MzkgMTU4LjAyMSw3LjY0IEMxNTguMDA1LDcuNTIzIDE1Ny45OTgsNy40MSAxNTcuOTk4LDcuMzA0IEwxNTcuOTk4LDYuNDE4IEMxNTcuOTk4LDUuODM0IDE1Ny42ODYsNS4yOTUgMTU3LjE4MSw1LjAwMiBDMTU2LjYyNCw0LjY4IDE1MC40NDIsMS4xMTEgMTUwLjQ0MiwxLjExMSBDMTUwLjE4OSwwLjk2NSAxNDkuOTA3LDAuODkyIDE0OS42MjUsMC44OTIiIGlkPSJGaWxsLTEiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTYuMDI3LDI1LjYzNiBMMTQyLjYwMyw1Mi41MjcgQzE0My44MDcsNTMuMjIyIDE0NC41ODIsNTQuMTE0IDE0NC44NDUsNTUuMDY4IEwxNDQuODM1LDU1LjA3NSBMNjMuNDYxLDEwMi4wNTcgTDYzLjQ2LDEwMi4wNTcgQzYxLjgwNiwxMDEuOTA1IDYwLjI2MSwxMDEuNDU3IDU5LjA1NywxMDAuNzYyIEwxMi40ODEsNzMuODcxIEw5Ni4wMjcsMjUuNjM2IiBpZD0iRmlsbC0yIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYzLjQ2MSwxMDIuMTc0IEM2My40NTMsMTAyLjE3NCA2My40NDYsMTAyLjE3NCA2My40MzksMTAyLjE3MiBDNjEuNzQ2LDEwMi4wMTYgNjAuMjExLDEwMS41NjMgNTguOTk4LDEwMC44NjMgTDEyLjQyMiw3My45NzMgQzEyLjM4Niw3My45NTIgMTIuMzY0LDczLjkxNCAxMi4zNjQsNzMuODcxIEMxMi4zNjQsNzMuODMgMTIuMzg2LDczLjc5MSAxMi40MjIsNzMuNzcgTDk1Ljk2OCwyNS41MzUgQzk2LjAwNCwyNS41MTQgOTYuMDQ5LDI1LjUxNCA5Ni4wODUsMjUuNTM1IEwxNDIuNjYxLDUyLjQyNiBDMTQzLjg4OCw1My4xMzQgMTQ0LjY4Miw1NC4wMzggMTQ0Ljk1Nyw1NS4wMzcgQzE0NC45Nyw1NS4wODMgMTQ0Ljk1Myw1NS4xMzMgMTQ0LjkxNSw1NS4xNjEgQzE0NC45MTEsNTUuMTY1IDE0NC44OTgsNTUuMTc0IDE0NC44OTQsNTUuMTc3IEw2My41MTksMTAyLjE1OCBDNjMuNTAxLDEwMi4xNjkgNjMuNDgxLDEwMi4xNzQgNjMuNDYxLDEwMi4xNzQgTDYzLjQ2MSwxMDIuMTc0IFogTTEyLjcxNCw3My44NzEgTDU5LjExNSwxMDAuNjYxIEM2MC4yOTMsMTAxLjM0MSA2MS43ODYsMTAxLjc4MiA2My40MzUsMTAxLjkzNyBMMTQ0LjcwNyw1NS4wMTUgQzE0NC40MjgsNTQuMTA4IDE0My42ODIsNTMuMjg1IDE0Mi41NDQsNTIuNjI4IEw5Ni4wMjcsMjUuNzcxIEwxMi43MTQsNzMuODcxIEwxMi43MTQsNzMuODcxIFoiIGlkPSJGaWxsLTMiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ4LjMyNyw1OC40NzEgQzE0OC4xNDUsNTguNDggMTQ3Ljk2Miw1OC40OCAxNDcuNzgxLDU4LjQ3MiBDMTQ1Ljg4Nyw1OC4zODkgMTQ0LjQ3OSw1Ny40MzQgMTQ0LjYzNiw1Ni4zNCBDMTQ0LjY4OSw1NS45NjcgMTQ0LjY2NCw1NS41OTcgMTQ0LjU2NCw1NS4yMzUgTDYzLjQ2MSwxMDIuMDU3IEM2NC4wODksMTAyLjExNSA2NC43MzMsMTAyLjEzIDY1LjM3OSwxMDIuMDk5IEM2NS41NjEsMTAyLjA5IDY1Ljc0MywxMDIuMDkgNjUuOTI1LDEwMi4wOTggQzY3LjgxOSwxMDIuMTgxIDY5LjIyNywxMDMuMTM2IDY5LjA3LDEwNC4yMyBMMTQ4LjMyNyw1OC40NzEiIGlkPSJGaWxsLTQiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNjkuMDcsMTA0LjM0NyBDNjkuMDQ4LDEwNC4zNDcgNjkuMDI1LDEwNC4zNCA2OS4wMDUsMTA0LjMyNyBDNjguOTY4LDEwNC4zMDEgNjguOTQ4LDEwNC4yNTcgNjguOTU1LDEwNC4yMTMgQzY5LDEwMy44OTYgNjguODk4LDEwMy41NzYgNjguNjU4LDEwMy4yODggQzY4LjE1MywxMDIuNjc4IDY3LjEwMywxMDIuMjY2IDY1LjkyLDEwMi4yMTQgQzY1Ljc0MiwxMDIuMjA2IDY1LjU2MywxMDIuMjA3IDY1LjM4NSwxMDIuMjE1IEM2NC43NDIsMTAyLjI0NiA2NC4wODcsMTAyLjIzMiA2My40NSwxMDIuMTc0IEM2My4zOTksMTAyLjE2OSA2My4zNTgsMTAyLjEzMiA2My4zNDcsMTAyLjA4MiBDNjMuMzM2LDEwMi4wMzMgNjMuMzU4LDEwMS45ODEgNjMuNDAyLDEwMS45NTYgTDE0NC41MDYsNTUuMTM0IEMxNDQuNTM3LDU1LjExNiAxNDQuNTc1LDU1LjExMyAxNDQuNjA5LDU1LjEyNyBDMTQ0LjY0Miw1NS4xNDEgMTQ0LjY2OCw1NS4xNyAxNDQuNjc3LDU1LjIwNCBDMTQ0Ljc4MSw1NS41ODUgMTQ0LjgwNiw1NS45NzIgMTQ0Ljc1MSw1Ni4zNTcgQzE0NC43MDYsNTYuNjczIDE0NC44MDgsNTYuOTk0IDE0NS4wNDcsNTcuMjgyIEMxNDUuNTUzLDU3Ljg5MiAxNDYuNjAyLDU4LjMwMyAxNDcuNzg2LDU4LjM1NSBDMTQ3Ljk2NCw1OC4zNjMgMTQ4LjE0Myw1OC4zNjMgMTQ4LjMyMSw1OC4zNTQgQzE0OC4zNzcsNTguMzUyIDE0OC40MjQsNTguMzg3IDE0OC40MzksNTguNDM4IEMxNDguNDU0LDU4LjQ5IDE0OC40MzIsNTguNTQ1IDE0OC4zODUsNTguNTcyIEw2OS4xMjksMTA0LjMzMSBDNjkuMTExLDEwNC4zNDIgNjkuMDksMTA0LjM0NyA2OS4wNywxMDQuMzQ3IEw2OS4wNywxMDQuMzQ3IFogTTY1LjY2NSwxMDEuOTc1IEM2NS43NTQsMTAxLjk3NSA2NS44NDIsMTAxLjk3NyA2NS45MywxMDEuOTgxIEM2Ny4xOTYsMTAyLjAzNyA2OC4yODMsMTAyLjQ2OSA2OC44MzgsMTAzLjEzOSBDNjkuMDY1LDEwMy40MTMgNjkuMTg4LDEwMy43MTQgNjkuMTk4LDEwNC4wMjEgTDE0Ny44ODMsNTguNTkyIEMxNDcuODQ3LDU4LjU5MiAxNDcuODExLDU4LjU5MSAxNDcuNzc2LDU4LjU4OSBDMTQ2LjUwOSw1OC41MzMgMTQ1LjQyMiw1OC4xIDE0NC44NjcsNTcuNDMxIEMxNDQuNTg1LDU3LjA5MSAxNDQuNDY1LDU2LjcwNyAxNDQuNTIsNTYuMzI0IEMxNDQuNTYzLDU2LjAyMSAxNDQuNTUyLDU1LjcxNiAxNDQuNDg4LDU1LjQxNCBMNjMuODQ2LDEwMS45NyBDNjQuMzUzLDEwMi4wMDIgNjQuODY3LDEwMi4wMDYgNjUuMzc0LDEwMS45ODIgQzY1LjQ3MSwxMDEuOTc3IDY1LjU2OCwxMDEuOTc1IDY1LjY2NSwxMDEuOTc1IEw2NS42NjUsMTAxLjk3NSBaIiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTIuMjA4LDU1LjEzNCBDMS4yMDcsNTMuMzA3IDEuOTY3LDUwLjkxNyAzLjkwNiw0OS43OTcgTDU5LjkxNywxNy40NTMgQzYxLjg1NiwxNi4zMzMgNjQuMjQxLDE2LjkwNyA2NS4yNDMsMTguNzM0IEw2NS40NzUsMTkuMTQ0IEM2NS44NzIsMTkuODgyIDY2LjM2OCwyMC41NiA2Ni45NDUsMjEuMTY1IEw2Ny4yMjMsMjEuNDM1IEM3MC41NDgsMjQuNjQ5IDc1LjgwNiwyNS4xNTEgODAuMTExLDIyLjY2NSBMODcuNDMsMTguNDQ1IEM4OS4zNywxNy4zMjYgOTEuNzU0LDE3Ljg5OSA5Mi43NTUsMTkuNzI3IEw5Ni4wMDUsMjUuNjU1IEwxMi40ODYsNzMuODg0IEwyLjIwOCw1NS4xMzQgWiIgaWQ9IkZpbGwtNiIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMi40ODYsNzQuMDAxIEMxMi40NzYsNzQuMDAxIDEyLjQ2NSw3My45OTkgMTIuNDU1LDczLjk5NiBDMTIuNDI0LDczLjk4OCAxMi4zOTksNzMuOTY3IDEyLjM4NCw3My45NCBMMi4xMDYsNTUuMTkgQzEuMDc1LDUzLjMxIDEuODU3LDUwLjg0NSAzLjg0OCw0OS42OTYgTDU5Ljg1OCwxNy4zNTIgQzYwLjUyNSwxNi45NjcgNjEuMjcxLDE2Ljc2NCA2Mi4wMTYsMTYuNzY0IEM2My40MzEsMTYuNzY0IDY0LjY2NiwxNy40NjYgNjUuMzI3LDE4LjY0NiBDNjUuMzM3LDE4LjY1NCA2NS4zNDUsMTguNjYzIDY1LjM1MSwxOC42NzQgTDY1LjU3OCwxOS4wODggQzY1LjU4NCwxOS4xIDY1LjU4OSwxOS4xMTIgNjUuNTkxLDE5LjEyNiBDNjUuOTg1LDE5LjgzOCA2Ni40NjksMjAuNDk3IDY3LjAzLDIxLjA4NSBMNjcuMzA1LDIxLjM1MSBDNjkuMTUxLDIzLjEzNyA3MS42NDksMjQuMTIgNzQuMzM2LDI0LjEyIEM3Ni4zMTMsMjQuMTIgNzguMjksMjMuNTgyIDgwLjA1MywyMi41NjMgQzgwLjA2NCwyMi41NTcgODAuMDc2LDIyLjU1MyA4MC4wODgsMjIuNTUgTDg3LjM3MiwxOC4zNDQgQzg4LjAzOCwxNy45NTkgODguNzg0LDE3Ljc1NiA4OS41MjksMTcuNzU2IEM5MC45NTYsMTcuNzU2IDkyLjIwMSwxOC40NzIgOTIuODU4LDE5LjY3IEw5Ni4xMDcsMjUuNTk5IEM5Ni4xMzgsMjUuNjU0IDk2LjExOCwyNS43MjQgOTYuMDYzLDI1Ljc1NiBMMTIuNTQ1LDczLjk4NSBDMTIuNTI2LDczLjk5NiAxMi41MDYsNzQuMDAxIDEyLjQ4Niw3NC4wMDEgTDEyLjQ4Niw3NC4wMDEgWiBNNjIuMDE2LDE2Ljk5NyBDNjEuMzEyLDE2Ljk5NyA2MC42MDYsMTcuMTkgNTkuOTc1LDE3LjU1NCBMMy45NjUsNDkuODk5IEMyLjA4Myw1MC45ODUgMS4zNDEsNTMuMzA4IDIuMzEsNTUuMDc4IEwxMi41MzEsNzMuNzIzIEw5NS44NDgsMjUuNjExIEw5Mi42NTMsMTkuNzgyIEM5Mi4wMzgsMTguNjYgOTAuODcsMTcuOTkgODkuNTI5LDE3Ljk5IEM4OC44MjUsMTcuOTkgODguMTE5LDE4LjE4MiA4Ny40ODksMTguNTQ3IEw4MC4xNzIsMjIuNzcyIEM4MC4xNjEsMjIuNzc4IDgwLjE0OSwyMi43ODIgODAuMTM3LDIyLjc4NSBDNzguMzQ2LDIzLjgxMSA3Ni4zNDEsMjQuMzU0IDc0LjMzNiwyNC4zNTQgQzcxLjU4OCwyNC4zNTQgNjkuMDMzLDIzLjM0NyA2Ny4xNDIsMjEuNTE5IEw2Ni44NjQsMjEuMjQ5IEM2Ni4yNzcsMjAuNjM0IDY1Ljc3NCwxOS45NDcgNjUuMzY3LDE5LjIwMyBDNjUuMzYsMTkuMTkyIDY1LjM1NiwxOS4xNzkgNjUuMzU0LDE5LjE2NiBMNjUuMTYzLDE4LjgxOSBDNjUuMTU0LDE4LjgxMSA2NS4xNDYsMTguODAxIDY1LjE0LDE4Ljc5IEM2NC41MjUsMTcuNjY3IDYzLjM1NywxNi45OTcgNjIuMDE2LDE2Ljk5NyBMNjIuMDE2LDE2Ljk5NyBaIiBpZD0iRmlsbC03IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTQyLjQzNCw0OC44MDggTDQyLjQzNCw0OC44MDggQzM5LjkyNCw0OC44MDcgMzcuNzM3LDQ3LjU1IDM2LjU4Miw0NS40NDMgQzM0Ljc3MSw0Mi4xMzkgMzYuMTQ0LDM3LjgwOSAzOS42NDEsMzUuNzg5IEw1MS45MzIsMjguNjkxIEM1My4xMDMsMjguMDE1IDU0LjQxMywyNy42NTggNTUuNzIxLDI3LjY1OCBDNTguMjMxLDI3LjY1OCA2MC40MTgsMjguOTE2IDYxLjU3MywzMS4wMjMgQzYzLjM4NCwzNC4zMjcgNjIuMDEyLDM4LjY1NyA1OC41MTQsNDAuNjc3IEw0Ni4yMjMsNDcuNzc1IEM0NS4wNTMsNDguNDUgNDMuNzQyLDQ4LjgwOCA0Mi40MzQsNDguODA4IEw0Mi40MzQsNDguODA4IFogTTU1LjcyMSwyOC4xMjUgQzU0LjQ5NSwyOC4xMjUgNTMuMjY1LDI4LjQ2MSA1Mi4xNjYsMjkuMDk2IEwzOS44NzUsMzYuMTk0IEMzNi41OTYsMzguMDg3IDM1LjMwMiw0Mi4xMzYgMzYuOTkyLDQ1LjIxOCBDMzguMDYzLDQ3LjE3MyA0MC4wOTgsNDguMzQgNDIuNDM0LDQ4LjM0IEM0My42NjEsNDguMzQgNDQuODksNDguMDA1IDQ1Ljk5LDQ3LjM3IEw1OC4yODEsNDAuMjcyIEM2MS41NiwzOC4zNzkgNjIuODUzLDM0LjMzIDYxLjE2NCwzMS4yNDggQzYwLjA5MiwyOS4yOTMgNTguMDU4LDI4LjEyNSA1NS43MjEsMjguMTI1IEw1NS43MjEsMjguMTI1IFoiIGlkPSJGaWxsLTgiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjU4OCwyLjQwNyBDMTQ5LjU4OCwyLjQwNyAxNTUuNzY4LDUuOTc1IDE1Ni4zMjUsNi4yOTcgTDE1Ni4zMjUsNy4xODQgQzE1Ni4zMjUsNy4zNiAxNTYuMzM4LDcuNTQ0IDE1Ni4zNjIsNy43MzMgQzE1Ni4zNzMsNy44MTQgMTU2LjM4Miw3Ljg5NCAxNTYuMzksNy45NzUgQzE1Ni41Myw5LjM5IDE1Ny4zNjMsMTAuOTczIDE1OC40OTUsMTEuOTc0IEwxNjUuODkxLDE4LjUxOSBDMTY2LjA2OCwxOC42NzUgMTY2LjI0OSwxOC44MTQgMTY2LjQzMiwxOC45MzQgQzE2OC4wMTEsMTkuOTc0IDE2OS4zODIsMTkuNCAxNjkuNDk0LDE3LjY1MiBDMTY5LjU0MywxNi44NjggMTY5LjU1MSwxNi4wNTcgMTY5LjUxNywxNS4yMjMgTDE2OS41MTQsMTUuMDYzIEwxNjkuNTE0LDEzLjkxMiBDMTcwLjc4LDE0LjY0MiAxOTUuNTAxLDI4LjkxNSAxOTUuNTAxLDI4LjkxNSBMMTk1LjUwMSw4Mi45MTUgQzE5NS41MDEsODQuMDA1IDE5NC43MzEsODQuNDQ1IDE5My43ODEsODMuODk3IEwxNTEuMzA4LDU5LjM3NCBDMTUwLjM1OCw1OC44MjYgMTQ5LjU4OCw1Ny40OTcgMTQ5LjU4OCw1Ni40MDggTDE0OS41ODgsMjIuMzc1IiBpZD0iRmlsbC05IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE5NC41NTMsODQuMjUgQzE5NC4yOTYsODQuMjUgMTk0LjAxMyw4NC4xNjUgMTkzLjcyMiw4My45OTcgTDE1MS4yNSw1OS40NzYgQzE1MC4yNjksNTguOTA5IDE0OS40NzEsNTcuNTMzIDE0OS40NzEsNTYuNDA4IEwxNDkuNDcxLDIyLjM3NSBMMTQ5LjcwNSwyMi4zNzUgTDE0OS43MDUsNTYuNDA4IEMxNDkuNzA1LDU3LjQ1OSAxNTAuNDUsNTguNzQ0IDE1MS4zNjYsNTkuMjc0IEwxOTMuODM5LDgzLjc5NSBDMTk0LjI2Myw4NC4wNCAxOTQuNjU1LDg0LjA4MyAxOTQuOTQyLDgzLjkxNyBDMTk1LjIyNyw4My43NTMgMTk1LjM4NCw4My4zOTcgMTk1LjM4NCw4Mi45MTUgTDE5NS4zODQsMjguOTgyIEMxOTQuMTAyLDI4LjI0MiAxNzIuMTA0LDE1LjU0MiAxNjkuNjMxLDE0LjExNCBMMTY5LjYzNCwxNS4yMiBDMTY5LjY2OCwxNi4wNTIgMTY5LjY2LDE2Ljg3NCAxNjkuNjEsMTcuNjU5IEMxNjkuNTU2LDE4LjUwMyAxNjkuMjE0LDE5LjEyMyAxNjguNjQ3LDE5LjQwNSBDMTY4LjAyOCwxOS43MTQgMTY3LjE5NywxOS41NzggMTY2LjM2NywxOS4wMzIgQzE2Ni4xODEsMTguOTA5IDE2NS45OTUsMTguNzY2IDE2NS44MTQsMTguNjA2IEwxNTguNDE3LDEyLjA2MiBDMTU3LjI1OSwxMS4wMzYgMTU2LjQxOCw5LjQzNyAxNTYuMjc0LDcuOTg2IEMxNTYuMjY2LDcuOTA3IDE1Ni4yNTcsNy44MjcgMTU2LjI0Nyw3Ljc0OCBDMTU2LjIyMSw3LjU1NSAxNTYuMjA5LDcuMzY1IDE1Ni4yMDksNy4xODQgTDE1Ni4yMDksNi4zNjQgQzE1NS4zNzUsNS44ODMgMTQ5LjUyOSwyLjUwOCAxNDkuNTI5LDIuNTA4IEwxNDkuNjQ2LDIuMzA2IEMxNDkuNjQ2LDIuMzA2IDE1NS44MjcsNS44NzQgMTU2LjM4NCw2LjE5NiBMMTU2LjQ0Miw2LjIzIEwxNTYuNDQyLDcuMTg0IEMxNTYuNDQyLDcuMzU1IDE1Ni40NTQsNy41MzUgMTU2LjQ3OCw3LjcxNyBDMTU2LjQ4OSw3LjggMTU2LjQ5OSw3Ljg4MiAxNTYuNTA3LDcuOTYzIEMxNTYuNjQ1LDkuMzU4IDE1Ny40NTUsMTAuODk4IDE1OC41NzIsMTEuODg2IEwxNjUuOTY5LDE4LjQzMSBDMTY2LjE0MiwxOC41ODQgMTY2LjMxOSwxOC43MiAxNjYuNDk2LDE4LjgzNyBDMTY3LjI1NCwxOS4zMzYgMTY4LDE5LjQ2NyAxNjguNTQzLDE5LjE5NiBDMTY5LjAzMywxOC45NTMgMTY5LjMyOSwxOC40MDEgMTY5LjM3NywxNy42NDUgQzE2OS40MjcsMTYuODY3IDE2OS40MzQsMTYuMDU0IDE2OS40MDEsMTUuMjI4IEwxNjkuMzk3LDE1LjA2NSBMMTY5LjM5NywxMy43MSBMMTY5LjU3MiwxMy44MSBDMTcwLjgzOSwxNC41NDEgMTk1LjU1OSwyOC44MTQgMTk1LjU1OSwyOC44MTQgTDE5NS42MTgsMjguODQ3IEwxOTUuNjE4LDgyLjkxNSBDMTk1LjYxOCw4My40ODQgMTk1LjQyLDgzLjkxMSAxOTUuMDU5LDg0LjExOSBDMTk0LjkwOCw4NC4yMDYgMTk0LjczNyw4NC4yNSAxOTQuNTUzLDg0LjI1IiBpZD0iRmlsbC0xMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDUuNjg1LDU2LjE2MSBMMTY5LjgsNzAuMDgzIEwxNDMuODIyLDg1LjA4MSBMMTQyLjM2LDg0Ljc3NCBDMTM1LjgyNiw4Mi42MDQgMTI4LjczMiw4MS4wNDYgMTIxLjM0MSw4MC4xNTggQzExNi45NzYsNzkuNjM0IDExMi42NzgsODEuMjU0IDExMS43NDMsODMuNzc4IEMxMTEuNTA2LDg0LjQxNCAxMTEuNTAzLDg1LjA3MSAxMTEuNzMyLDg1LjcwNiBDMTEzLjI3LDg5Ljk3MyAxMTUuOTY4LDk0LjA2OSAxMTkuNzI3LDk3Ljg0MSBMMTIwLjI1OSw5OC42ODYgQzEyMC4yNiw5OC42ODUgOTQuMjgyLDExMy42ODMgOTQuMjgyLDExMy42ODMgTDcwLjE2Nyw5OS43NjEgTDE0NS42ODUsNTYuMTYxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik05NC4yODIsMTEzLjgxOCBMOTQuMjIzLDExMy43ODUgTDY5LjkzMyw5OS43NjEgTDcwLjEwOCw5OS42NiBMMTQ1LjY4NSw1Ni4wMjYgTDE0NS43NDMsNTYuMDU5IEwxNzAuMDMzLDcwLjA4MyBMMTQzLjg0Miw4NS4yMDUgTDE0My43OTcsODUuMTk1IEMxNDMuNzcyLDg1LjE5IDE0Mi4zMzYsODQuODg4IDE0Mi4zMzYsODQuODg4IEMxMzUuNzg3LDgyLjcxNCAxMjguNzIzLDgxLjE2MyAxMjEuMzI3LDgwLjI3NCBDMTIwLjc4OCw4MC4yMDkgMTIwLjIzNiw4MC4xNzcgMTE5LjY4OSw4MC4xNzcgQzExNS45MzEsODAuMTc3IDExMi42MzUsODEuNzA4IDExMS44NTIsODMuODE5IEMxMTEuNjI0LDg0LjQzMiAxMTEuNjIxLDg1LjA1MyAxMTEuODQyLDg1LjY2NyBDMTEzLjM3Nyw4OS45MjUgMTE2LjA1OCw5My45OTMgMTE5LjgxLDk3Ljc1OCBMMTE5LjgyNiw5Ny43NzkgTDEyMC4zNTIsOTguNjE0IEMxMjAuMzU0LDk4LjYxNyAxMjAuMzU2LDk4LjYyIDEyMC4zNTgsOTguNjI0IEwxMjAuNDIyLDk4LjcyNiBMMTIwLjMxNyw5OC43ODcgQzEyMC4yNjQsOTguODE4IDk0LjU5OSwxMTMuNjM1IDk0LjM0LDExMy43ODUgTDk0LjI4MiwxMTMuODE4IEw5NC4yODIsMTEzLjgxOCBaIE03MC40MDEsOTkuNzYxIEw5NC4yODIsMTEzLjU0OSBMMTE5LjA4NCw5OS4yMjkgQzExOS42Myw5OC45MTQgMTE5LjkzLDk4Ljc0IDEyMC4xMDEsOTguNjU0IEwxMTkuNjM1LDk3LjkxNCBDMTE1Ljg2NCw5NC4xMjcgMTEzLjE2OCw5MC4wMzMgMTExLjYyMiw4NS43NDYgQzExMS4zODIsODUuMDc5IDExMS4zODYsODQuNDA0IDExMS42MzMsODMuNzM4IEMxMTIuNDQ4LDgxLjUzOSAxMTUuODM2LDc5Ljk0MyAxMTkuNjg5LDc5Ljk0MyBDMTIwLjI0Niw3OS45NDMgMTIwLjgwNiw3OS45NzYgMTIxLjM1NSw4MC4wNDIgQzEyOC43NjcsODAuOTMzIDEzNS44NDYsODIuNDg3IDE0Mi4zOTYsODQuNjYzIEMxNDMuMjMyLDg0LjgzOCAxNDMuNjExLDg0LjkxNyAxNDMuNzg2LDg0Ljk2NyBMMTY5LjU2Niw3MC4wODMgTDE0NS42ODUsNTYuMjk1IEw3MC40MDEsOTkuNzYxIEw3MC40MDEsOTkuNzYxIFoiIGlkPSJGaWxsLTEyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2Ny4yMywxOC45NzkgTDE2Ny4yMyw2OS44NSBMMTM5LjkwOSw4NS42MjMgTDEzMy40NDgsNzEuNDU2IEMxMzIuNTM4LDY5LjQ2IDEzMC4wMiw2OS43MTggMTI3LjgyNCw3Mi4wMyBDMTI2Ljc2OSw3My4xNCAxMjUuOTMxLDc0LjU4NSAxMjUuNDk0LDc2LjA0OCBMMTE5LjAzNCw5Ny42NzYgTDkxLjcxMiwxMTMuNDUgTDkxLjcxMiw2Mi41NzkgTDE2Ny4yMywxOC45NzkiIGlkPSJGaWxsLTEzIiBmaWxsPSIjRkZGRkZGIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTkxLjcxMiwxMTMuNTY3IEM5MS42OTIsMTEzLjU2NyA5MS42NzIsMTEzLjU2MSA5MS42NTMsMTEzLjU1MSBDOTEuNjE4LDExMy41MyA5MS41OTUsMTEzLjQ5MiA5MS41OTUsMTEzLjQ1IEw5MS41OTUsNjIuNTc5IEM5MS41OTUsNjIuNTM3IDkxLjYxOCw2Mi40OTkgOTEuNjUzLDYyLjQ3OCBMMTY3LjE3MiwxOC44NzggQzE2Ny4yMDgsMTguODU3IDE2Ny4yNTIsMTguODU3IDE2Ny4yODgsMTguODc4IEMxNjcuMzI0LDE4Ljg5OSAxNjcuMzQ3LDE4LjkzNyAxNjcuMzQ3LDE4Ljk3OSBMMTY3LjM0Nyw2OS44NSBDMTY3LjM0Nyw2OS44OTEgMTY3LjMyNCw2OS45MyAxNjcuMjg4LDY5Ljk1IEwxMzkuOTY3LDg1LjcyNSBDMTM5LjkzOSw4NS43NDEgMTM5LjkwNSw4NS43NDUgMTM5Ljg3Myw4NS43MzUgQzEzOS44NDIsODUuNzI1IDEzOS44MTYsODUuNzAyIDEzOS44MDIsODUuNjcyIEwxMzMuMzQyLDcxLjUwNCBDMTMyLjk2Nyw3MC42ODIgMTMyLjI4LDcwLjIyOSAxMzEuNDA4LDcwLjIyOSBDMTMwLjMxOSw3MC4yMjkgMTI5LjA0NCw3MC45MTUgMTI3LjkwOCw3Mi4xMSBDMTI2Ljg3NCw3My4yIDEyNi4wMzQsNzQuNjQ3IDEyNS42MDYsNzYuMDgyIEwxMTkuMTQ2LDk3LjcwOSBDMTE5LjEzNyw5Ny43MzggMTE5LjExOCw5Ny43NjIgMTE5LjA5Miw5Ny43NzcgTDkxLjc3LDExMy41NTEgQzkxLjc1MiwxMTMuNTYxIDkxLjczMiwxMTMuNTY3IDkxLjcxMiwxMTMuNTY3IEw5MS43MTIsMTEzLjU2NyBaIE05MS44MjksNjIuNjQ3IEw5MS44MjksMTEzLjI0OCBMMTE4LjkzNSw5Ny41OTggTDEyNS4zODIsNzYuMDE1IEMxMjUuODI3LDc0LjUyNSAxMjYuNjY0LDczLjA4MSAxMjcuNzM5LDcxLjk1IEMxMjguOTE5LDcwLjcwOCAxMzAuMjU2LDY5Ljk5NiAxMzEuNDA4LDY5Ljk5NiBDMTMyLjM3Nyw2OS45OTYgMTMzLjEzOSw3MC40OTcgMTMzLjU1NCw3MS40MDcgTDEzOS45NjEsODUuNDU4IEwxNjcuMTEzLDY5Ljc4MiBMMTY3LjExMywxOS4xODEgTDkxLjgyOSw2Mi42NDcgTDkxLjgyOSw2Mi42NDcgWiIgaWQ9IkZpbGwtMTQiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTY4LjU0MywxOS4yMTMgTDE2OC41NDMsNzAuMDgzIEwxNDEuMjIxLDg1Ljg1NyBMMTM0Ljc2MSw3MS42ODkgQzEzMy44NTEsNjkuNjk0IDEzMS4zMzMsNjkuOTUxIDEyOS4xMzcsNzIuMjYzIEMxMjguMDgyLDczLjM3NCAxMjcuMjQ0LDc0LjgxOSAxMjYuODA3LDc2LjI4MiBMMTIwLjM0Niw5Ny45MDkgTDkzLjAyNSwxMTMuNjgzIEw5My4wMjUsNjIuODEzIEwxNjguNTQzLDE5LjIxMyIgaWQ9IkZpbGwtMTUiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTMuMDI1LDExMy44IEM5My4wMDUsMTEzLjggOTIuOTg0LDExMy43OTUgOTIuOTY2LDExMy43ODUgQzkyLjkzMSwxMTMuNzY0IDkyLjkwOCwxMTMuNzI1IDkyLjkwOCwxMTMuNjg0IEw5Mi45MDgsNjIuODEzIEM5Mi45MDgsNjIuNzcxIDkyLjkzMSw2Mi43MzMgOTIuOTY2LDYyLjcxMiBMMTY4LjQ4NCwxOS4xMTIgQzE2OC41MiwxOS4wOSAxNjguNTY1LDE5LjA5IDE2OC42MDEsMTkuMTEyIEMxNjguNjM3LDE5LjEzMiAxNjguNjYsMTkuMTcxIDE2OC42NiwxOS4yMTIgTDE2OC42Niw3MC4wODMgQzE2OC42Niw3MC4xMjUgMTY4LjYzNyw3MC4xNjQgMTY4LjYwMSw3MC4xODQgTDE0MS4yOCw4NS45NTggQzE0MS4yNTEsODUuOTc1IDE0MS4yMTcsODUuOTc5IDE0MS4xODYsODUuOTY4IEMxNDEuMTU0LDg1Ljk1OCAxNDEuMTI5LDg1LjkzNiAxNDEuMTE1LDg1LjkwNiBMMTM0LjY1NSw3MS43MzggQzEzNC4yOCw3MC45MTUgMTMzLjU5Myw3MC40NjMgMTMyLjcyLDcwLjQ2MyBDMTMxLjYzMiw3MC40NjMgMTMwLjM1Nyw3MS4xNDggMTI5LjIyMSw3Mi4zNDQgQzEyOC4xODYsNzMuNDMzIDEyNy4zNDcsNzQuODgxIDEyNi45MTksNzYuMzE1IEwxMjAuNDU4LDk3Ljk0MyBDMTIwLjQ1LDk3Ljk3MiAxMjAuNDMxLDk3Ljk5NiAxMjAuNDA1LDk4LjAxIEw5My4wODMsMTEzLjc4NSBDOTMuMDY1LDExMy43OTUgOTMuMDQ1LDExMy44IDkzLjAyNSwxMTMuOCBMOTMuMDI1LDExMy44IFogTTkzLjE0Miw2Mi44ODEgTDkzLjE0MiwxMTMuNDgxIEwxMjAuMjQ4LDk3LjgzMiBMMTI2LjY5NSw3Ni4yNDggQzEyNy4xNCw3NC43NTggMTI3Ljk3Nyw3My4zMTUgMTI5LjA1Miw3Mi4xODMgQzEzMC4yMzEsNzAuOTQyIDEzMS41NjgsNzAuMjI5IDEzMi43Miw3MC4yMjkgQzEzMy42ODksNzAuMjI5IDEzNC40NTIsNzAuNzMxIDEzNC44NjcsNzEuNjQxIEwxNDEuMjc0LDg1LjY5MiBMMTY4LjQyNiw3MC4wMTYgTDE2OC40MjYsMTkuNDE1IEw5My4xNDIsNjIuODgxIEw5My4xNDIsNjIuODgxIFoiIGlkPSJGaWxsLTE2IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS44LDcwLjA4MyBMMTQyLjQ3OCw4NS44NTcgTDEzNi4wMTgsNzEuNjg5IEMxMzUuMTA4LDY5LjY5NCAxMzIuNTksNjkuOTUxIDEzMC4zOTMsNzIuMjYzIEMxMjkuMzM5LDczLjM3NCAxMjguNSw3NC44MTkgMTI4LjA2NCw3Ni4yODIgTDEyMS42MDMsOTcuOTA5IEw5NC4yODIsMTEzLjY4MyBMOTQuMjgyLDYyLjgxMyBMMTY5LjgsMTkuMjEzIEwxNjkuOCw3MC4wODMgWiIgaWQ9IkZpbGwtMTciIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTQuMjgyLDExMy45MTcgQzk0LjI0MSwxMTMuOTE3IDk0LjIwMSwxMTMuOTA3IDk0LjE2NSwxMTMuODg2IEM5NC4wOTMsMTEzLjg0NSA5NC4wNDgsMTEzLjc2NyA5NC4wNDgsMTEzLjY4NCBMOTQuMDQ4LDYyLjgxMyBDOTQuMDQ4LDYyLjczIDk0LjA5Myw2Mi42NTIgOTQuMTY1LDYyLjYxMSBMMTY5LjY4MywxOS4wMSBDMTY5Ljc1NSwxOC45NjkgMTY5Ljg0NCwxOC45NjkgMTY5LjkxNywxOS4wMSBDMTY5Ljk4OSwxOS4wNTIgMTcwLjAzMywxOS4xMjkgMTcwLjAzMywxOS4yMTIgTDE3MC4wMzMsNzAuMDgzIEMxNzAuMDMzLDcwLjE2NiAxNjkuOTg5LDcwLjI0NCAxNjkuOTE3LDcwLjI4NSBMMTQyLjU5NSw4Ni4wNiBDMTQyLjUzOCw4Ni4wOTIgMTQyLjQ2OSw4Ni4xIDE0Mi40MDcsODYuMDggQzE0Mi4zNDQsODYuMDYgMTQyLjI5Myw4Ni4wMTQgMTQyLjI2Niw4NS45NTQgTDEzNS44MDUsNzEuNzg2IEMxMzUuNDQ1LDcwLjk5NyAxMzQuODEzLDcwLjU4IDEzMy45NzcsNzAuNTggQzEzMi45MjEsNzAuNTggMTMxLjY3Niw3MS4yNTIgMTMwLjU2Miw3Mi40MjQgQzEyOS41NCw3My41MDEgMTI4LjcxMSw3NC45MzEgMTI4LjI4Nyw3Ni4zNDggTDEyMS44MjcsOTcuOTc2IEMxMjEuODEsOTguMDM0IDEyMS43NzEsOTguMDgyIDEyMS43Miw5OC4xMTIgTDk0LjM5OCwxMTMuODg2IEM5NC4zNjIsMTEzLjkwNyA5NC4zMjIsMTEzLjkxNyA5NC4yODIsMTEzLjkxNyBMOTQuMjgyLDExMy45MTcgWiBNOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDExMy4yNzkgTDEyMS40MDYsOTcuNzU0IEwxMjcuODQsNzYuMjE1IEMxMjguMjksNzQuNzA4IDEyOS4xMzcsNzMuMjQ3IDEzMC4yMjQsNzIuMTAzIEMxMzEuNDI1LDcwLjgzOCAxMzIuNzkzLDcwLjExMiAxMzMuOTc3LDcwLjExMiBDMTM0Ljk5NSw3MC4xMTIgMTM1Ljc5NSw3MC42MzggMTM2LjIzLDcxLjU5MiBMMTQyLjU4NCw4NS41MjYgTDE2OS41NjYsNjkuOTQ4IEwxNjkuNTY2LDE5LjYxNyBMOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDYyLjk0OCBaIiBpZD0iRmlsbC0xOCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMDkuODk0LDkyLjk0MyBMMTA5Ljg5NCw5Mi45NDMgQzEwOC4xMiw5Mi45NDMgMTA2LjY1Myw5Mi4yMTggMTA1LjY1LDkwLjgyMyBDMTA1LjU4Myw5MC43MzEgMTA1LjU5Myw5MC42MSAxMDUuNjczLDkwLjUyOSBDMTA1Ljc1Myw5MC40NDggMTA1Ljg4LDkwLjQ0IDEwNS45NzQsOTAuNTA2IEMxMDYuNzU0LDkxLjA1MyAxMDcuNjc5LDkxLjMzMyAxMDguNzI0LDkxLjMzMyBDMTEwLjA0Nyw5MS4zMzMgMTExLjQ3OCw5MC44OTQgMTEyLjk4LDkwLjAyNyBDMTE4LjI5MSw4Ni45NiAxMjIuNjExLDc5LjUwOSAxMjIuNjExLDczLjQxNiBDMTIyLjYxMSw3MS40ODkgMTIyLjE2OSw2OS44NTYgMTIxLjMzMyw2OC42OTIgQzEyMS4yNjYsNjguNiAxMjEuMjc2LDY4LjQ3MyAxMjEuMzU2LDY4LjM5MiBDMTIxLjQzNiw2OC4zMTEgMTIxLjU2Myw2OC4yOTkgMTIxLjY1Niw2OC4zNjUgQzEyMy4zMjcsNjkuNTM3IDEyNC4yNDcsNzEuNzQ2IDEyNC4yNDcsNzQuNTg0IEMxMjQuMjQ3LDgwLjgyNiAxMTkuODIxLDg4LjQ0NyAxMTQuMzgyLDkxLjU4NyBDMTEyLjgwOCw5Mi40OTUgMTExLjI5OCw5Mi45NDMgMTA5Ljg5NCw5Mi45NDMgTDEwOS44OTQsOTIuOTQzIFogTTEwNi45MjUsOTEuNDAxIEMxMDcuNzM4LDkyLjA1MiAxMDguNzQ1LDkyLjI3OCAxMDkuODkzLDkyLjI3OCBMMTA5Ljg5NCw5Mi4yNzggQzExMS4yMTUsOTIuMjc4IDExMi42NDcsOTEuOTUxIDExNC4xNDgsOTEuMDg0IEMxMTkuNDU5LDg4LjAxNyAxMjMuNzgsODAuNjIxIDEyMy43OCw3NC41MjggQzEyMy43OCw3Mi41NDkgMTIzLjMxNyw3MC45MjkgMTIyLjQ1NCw2OS43NjcgQzEyMi44NjUsNzAuODAyIDEyMy4wNzksNzIuMDQyIDEyMy4wNzksNzMuNDAyIEMxMjMuMDc5LDc5LjY0NSAxMTguNjUzLDg3LjI4NSAxMTMuMjE0LDkwLjQyNSBDMTExLjY0LDkxLjMzNCAxMTAuMTMsOTEuNzQyIDEwOC43MjQsOTEuNzQyIEMxMDguMDgzLDkxLjc0MiAxMDcuNDgxLDkxLjU5MyAxMDYuOTI1LDkxLjQwMSBMMTA2LjkyNSw5MS40MDEgWiIgaWQ9IkZpbGwtMTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjA5Nyw5MC4yMyBDMTE4LjQ4MSw4Ny4xMjIgMTIyLjg0NSw3OS41OTQgMTIyLjg0NSw3My40MTYgQzEyMi44NDUsNzEuMzY1IDEyMi4zNjIsNjkuNzI0IDEyMS41MjIsNjguNTU2IEMxMTkuNzM4LDY3LjMwNCAxMTcuMTQ4LDY3LjM2MiAxMTQuMjY1LDY5LjAyNiBDMTA4Ljg4MSw3Mi4xMzQgMTA0LjUxNyw3OS42NjIgMTA0LjUxNyw4NS44NCBDMTA0LjUxNyw4Ny44OTEgMTA1LDg5LjUzMiAxMDUuODQsOTAuNyBDMTA3LjYyNCw5MS45NTIgMTEwLjIxNCw5MS44OTQgMTEzLjA5Nyw5MC4yMyIgaWQ9IkZpbGwtMjAiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTA4LjcyNCw5MS42MTQgTDEwOC43MjQsOTEuNjE0IEMxMDcuNTgyLDkxLjYxNCAxMDYuNTY2LDkxLjQwMSAxMDUuNzA1LDkwLjc5NyBDMTA1LjY4NCw5MC43ODMgMTA1LjY2NSw5MC44MTEgMTA1LjY1LDkwLjc5IEMxMDQuNzU2LDg5LjU0NiAxMDQuMjgzLDg3Ljg0MiAxMDQuMjgzLDg1LjgxNyBDMTA0LjI4Myw3OS41NzUgMTA4LjcwOSw3MS45NTMgMTE0LjE0OCw2OC44MTIgQzExNS43MjIsNjcuOTA0IDExNy4yMzIsNjcuNDQ5IDExOC42MzgsNjcuNDQ5IEMxMTkuNzgsNjcuNDQ5IDEyMC43OTYsNjcuNzU4IDEyMS42NTYsNjguMzYyIEMxMjEuNjc4LDY4LjM3NyAxMjEuNjk3LDY4LjM5NyAxMjEuNzEyLDY4LjQxOCBDMTIyLjYwNiw2OS42NjIgMTIzLjA3OSw3MS4zOSAxMjMuMDc5LDczLjQxNSBDMTIzLjA3OSw3OS42NTggMTE4LjY1Myw4Ny4xOTggMTEzLjIxNCw5MC4zMzggQzExMS42NCw5MS4yNDcgMTEwLjEzLDkxLjYxNCAxMDguNzI0LDkxLjYxNCBMMTA4LjcyNCw5MS42MTQgWiBNMTA2LjAwNiw5MC41MDUgQzEwNi43OCw5MS4wMzcgMTA3LjY5NCw5MS4yODEgMTA4LjcyNCw5MS4yODEgQzExMC4wNDcsOTEuMjgxIDExMS40NzgsOTAuODY4IDExMi45OCw5MC4wMDEgQzExOC4yOTEsODYuOTM1IDEyMi42MTEsNzkuNDk2IDEyMi42MTEsNzMuNDAzIEMxMjIuNjExLDcxLjQ5NCAxMjIuMTc3LDY5Ljg4IDEyMS4zNTYsNjguNzE4IEMxMjAuNTgyLDY4LjE4NSAxMTkuNjY4LDY3LjkxOSAxMTguNjM4LDY3LjkxOSBDMTE3LjMxNSw2Ny45MTkgMTE1Ljg4Myw2OC4zNiAxMTQuMzgyLDY5LjIyNyBDMTA5LjA3MSw3Mi4yOTMgMTA0Ljc1MSw3OS43MzMgMTA0Ljc1MSw4NS44MjYgQzEwNC43NTEsODcuNzM1IDEwNS4xODUsODkuMzQzIDEwNi4wMDYsOTAuNTA1IEwxMDYuMDA2LDkwLjUwNSBaIiBpZD0iRmlsbC0yMSIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDkuMzE4LDcuMjYyIEwxMzkuMzM0LDE2LjE0IEwxNTUuMjI3LDI3LjE3MSBMMTYwLjgxNiwyMS4wNTkgTDE0OS4zMTgsNy4yNjIiIGlkPSJGaWxsLTIyIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS42NzYsMTMuODQgTDE1OS45MjgsMTkuNDY3IEMxNTYuMjg2LDIxLjU3IDE1MC40LDIxLjU4IDE0Ni43ODEsMTkuNDkxIEMxNDMuMTYxLDE3LjQwMiAxNDMuMTgsMTQuMDAzIDE0Ni44MjIsMTEuOSBMMTU2LjMxNyw2LjI5MiBMMTQ5LjU4OCwyLjQwNyBMNjcuNzUyLDQ5LjQ3OCBMMTEzLjY3NSw3NS45OTIgTDExNi43NTYsNzQuMjEzIEMxMTcuMzg3LDczLjg0OCAxMTcuNjI1LDczLjMxNSAxMTcuMzc0LDcyLjgyMyBDMTE1LjAxNyw2OC4xOTEgMTE0Ljc4MSw2My4yNzcgMTE2LjY5MSw1OC41NjEgQzEyMi4zMjksNDQuNjQxIDE0MS4yLDMzLjc0NiAxNjUuMzA5LDMwLjQ5MSBDMTczLjQ3OCwyOS4zODggMTgxLjk4OSwyOS41MjQgMTkwLjAxMywzMC44ODUgQzE5MC44NjUsMzEuMDMgMTkxLjc4OSwzMC44OTMgMTkyLjQyLDMwLjUyOCBMMTk1LjUwMSwyOC43NSBMMTY5LjY3NiwxMy44NCIgaWQ9IkZpbGwtMjMiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3Ni40NTkgQzExMy41OTQsNzYuNDU5IDExMy41MTQsNzYuNDM4IDExMy40NDIsNzYuMzk3IEw2Ny41MTgsNDkuODgyIEM2Ny4zNzQsNDkuNzk5IDY3LjI4NCw0OS42NDUgNjcuMjg1LDQ5LjQ3OCBDNjcuMjg1LDQ5LjMxMSA2Ny4zNzQsNDkuMTU3IDY3LjUxOSw0OS4wNzMgTDE0OS4zNTUsMi4wMDIgQzE0OS40OTksMS45MTkgMTQ5LjY3NywxLjkxOSAxNDkuODIxLDIuMDAyIEwxNTYuNTUsNS44ODcgQzE1Ni43NzQsNi4wMTcgMTU2Ljg1LDYuMzAyIDE1Ni43MjIsNi41MjYgQzE1Ni41OTIsNi43NDkgMTU2LjMwNyw2LjgyNiAxNTYuMDgzLDYuNjk2IEwxNDkuNTg3LDIuOTQ2IEw2OC42ODcsNDkuNDc5IEwxMTMuNjc1LDc1LjQ1MiBMMTE2LjUyMyw3My44MDggQzExNi43MTUsNzMuNjk3IDExNy4xNDMsNzMuMzk5IDExNi45NTgsNzMuMDM1IEMxMTQuNTQyLDY4LjI4NyAxMTQuMyw2My4yMjEgMTE2LjI1OCw1OC4zODUgQzExOS4wNjQsNTEuNDU4IDEyNS4xNDMsNDUuMTQzIDEzMy44NCw0MC4xMjIgQzE0Mi40OTcsMzUuMTI0IDE1My4zNTgsMzEuNjMzIDE2NS4yNDcsMzAuMDI4IEMxNzMuNDQ1LDI4LjkyMSAxODIuMDM3LDI5LjA1OCAxOTAuMDkxLDMwLjQyNSBDMTkwLjgzLDMwLjU1IDE5MS42NTIsMzAuNDMyIDE5Mi4xODYsMzAuMTI0IEwxOTQuNTY3LDI4Ljc1IEwxNjkuNDQyLDE0LjI0NCBDMTY5LjIxOSwxNC4xMTUgMTY5LjE0MiwxMy44MjkgMTY5LjI3MSwxMy42MDYgQzE2OS40LDEzLjM4MiAxNjkuNjg1LDEzLjMwNiAxNjkuOTA5LDEzLjQzNSBMMTk1LjczNCwyOC4zNDUgQzE5NS44NzksMjguNDI4IDE5NS45NjgsMjguNTgzIDE5NS45NjgsMjguNzUgQzE5NS45NjgsMjguOTE2IDE5NS44NzksMjkuMDcxIDE5NS43MzQsMjkuMTU0IEwxOTIuNjUzLDMwLjkzMyBDMTkxLjkzMiwzMS4zNSAxOTAuODksMzEuNTA4IDE4OS45MzUsMzEuMzQ2IEMxODEuOTcyLDI5Ljk5NSAxNzMuNDc4LDI5Ljg2IDE2NS4zNzIsMzAuOTU0IEMxNTMuNjAyLDMyLjU0MyAxNDIuODYsMzUuOTkzIDEzNC4zMDcsNDAuOTMxIEMxMjUuNzkzLDQ1Ljg0NyAxMTkuODUxLDUyLjAwNCAxMTcuMTI0LDU4LjczNiBDMTE1LjI3LDYzLjMxNCAxMTUuNTAxLDY4LjExMiAxMTcuNzksNzIuNjExIEMxMTguMTYsNzMuMzM2IDExNy44NDUsNzQuMTI0IDExNi45OSw3NC42MTcgTDExMy45MDksNzYuMzk3IEMxMTMuODM2LDc2LjQzOCAxMTMuNzU2LDc2LjQ1OSAxMTMuNjc1LDc2LjQ1OSIgaWQ9IkZpbGwtMjQiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUzLjMxNiwyMS4yNzkgQzE1MC45MDMsMjEuMjc5IDE0OC40OTUsMjAuNzUxIDE0Ni42NjQsMTkuNjkzIEMxNDQuODQ2LDE4LjY0NCAxNDMuODQ0LDE3LjIzMiAxNDMuODQ0LDE1LjcxOCBDMTQzLjg0NCwxNC4xOTEgMTQ0Ljg2LDEyLjc2MyAxNDYuNzA1LDExLjY5OCBMMTU2LjE5OCw2LjA5MSBDMTU2LjMwOSw2LjAyNSAxNTYuNDUyLDYuMDYyIDE1Ni41MTgsNi4xNzMgQzE1Ni41ODMsNi4yODQgMTU2LjU0Nyw2LjQyNyAxNTYuNDM2LDYuNDkzIEwxNDYuOTQsMTIuMTAyIEMxNDUuMjQ0LDEzLjA4MSAxNDQuMzEyLDE0LjM2NSAxNDQuMzEyLDE1LjcxOCBDMTQ0LjMxMiwxNy4wNTggMTQ1LjIzLDE4LjMyNiAxNDYuODk3LDE5LjI4OSBDMTUwLjQ0NiwyMS4zMzggMTU2LjI0LDIxLjMyNyAxNTkuODExLDE5LjI2NSBMMTY5LjU1OSwxMy42MzcgQzE2OS42NywxMy41NzMgMTY5LjgxMywxMy42MTEgMTY5Ljg3OCwxMy43MjMgQzE2OS45NDMsMTMuODM0IDE2OS45MDQsMTMuOTc3IDE2OS43OTMsMTQuMDQyIEwxNjAuMDQ1LDE5LjY3IEMxNTguMTg3LDIwLjc0MiAxNTUuNzQ5LDIxLjI3OSAxNTMuMzE2LDIxLjI3OSIgaWQ9IkZpbGwtMjUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3NS45OTIgTDY3Ljc2Miw0OS40ODQiIGlkPSJGaWxsLTI2IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMy42NzUsNzYuMzQyIEMxMTMuNjE1LDc2LjM0MiAxMTMuNTU1LDc2LjMyNyAxMTMuNSw3Ni4yOTUgTDY3LjU4Nyw0OS43ODcgQzY3LjQxOSw0OS42OSA2Ny4zNjIsNDkuNDc2IDY3LjQ1OSw0OS4zMDkgQzY3LjU1Niw0OS4xNDEgNjcuNzcsNDkuMDgzIDY3LjkzNyw0OS4xOCBMMTEzLjg1LDc1LjY4OCBDMTE0LjAxOCw3NS43ODUgMTE0LjA3NSw3NiAxMTMuOTc4LDc2LjE2NyBDMTEzLjkxNCw3Ni4yNzkgMTEzLjc5Niw3Ni4zNDIgMTEzLjY3NSw3Ni4zNDIiIGlkPSJGaWxsLTI3IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY3Ljc2Miw0OS40ODQgTDY3Ljc2MiwxMDMuNDg1IEM2Ny43NjIsMTA0LjU3NSA2OC41MzIsMTA1LjkwMyA2OS40ODIsMTA2LjQ1MiBMMTExLjk1NSwxMzAuOTczIEMxMTIuOTA1LDEzMS41MjIgMTEzLjY3NSwxMzEuMDgzIDExMy42NzUsMTI5Ljk5MyBMMTEzLjY3NSw3NS45OTIiIGlkPSJGaWxsLTI4IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMi43MjcsMTMxLjU2MSBDMTEyLjQzLDEzMS41NjEgMTEyLjEwNywxMzEuNDY2IDExMS43OCwxMzEuMjc2IEw2OS4zMDcsMTA2Ljc1NSBDNjguMjQ0LDEwNi4xNDIgNjcuNDEyLDEwNC43MDUgNjcuNDEyLDEwMy40ODUgTDY3LjQxMiw0OS40ODQgQzY3LjQxMiw0OS4yOSA2Ny41NjksNDkuMTM0IDY3Ljc2Miw0OS4xMzQgQzY3Ljk1Niw0OS4xMzQgNjguMTEzLDQ5LjI5IDY4LjExMyw0OS40ODQgTDY4LjExMywxMDMuNDg1IEM2OC4xMTMsMTA0LjQ0NSA2OC44MiwxMDUuNjY1IDY5LjY1NywxMDYuMTQ4IEwxMTIuMTMsMTMwLjY3IEMxMTIuNDc0LDEzMC44NjggMTEyLjc5MSwxMzAuOTEzIDExMywxMzAuNzkyIEMxMTMuMjA2LDEzMC42NzMgMTEzLjMyNSwxMzAuMzgxIDExMy4zMjUsMTI5Ljk5MyBMMTEzLjMyNSw3NS45OTIgQzExMy4zMjUsNzUuNzk4IDExMy40ODIsNzUuNjQxIDExMy42NzUsNzUuNjQxIEMxMTMuODY5LDc1LjY0MSAxMTQuMDI1LDc1Ljc5OCAxMTQuMDI1LDc1Ljk5MiBMMTE0LjAyNSwxMjkuOTkzIEMxMTQuMDI1LDEzMC42NDggMTEzLjc4NiwxMzEuMTQ3IDExMy4zNSwxMzEuMzk5IEMxMTMuMTYyLDEzMS41MDcgMTEyLjk1MiwxMzEuNTYxIDExMi43MjcsMTMxLjU2MSIgaWQ9IkZpbGwtMjkiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEyLjg2LDQwLjUxMiBDMTEyLjg2LDQwLjUxMiAxMTIuODYsNDAuNTEyIDExMi44NTksNDAuNTEyIEMxMTAuNTQxLDQwLjUxMiAxMDguMzYsMzkuOTkgMTA2LjcxNywzOS4wNDEgQzEwNS4wMTIsMzguMDU3IDEwNC4wNzQsMzYuNzI2IDEwNC4wNzQsMzUuMjkyIEMxMDQuMDc0LDMzLjg0NyAxMDUuMDI2LDMyLjUwMSAxMDYuNzU0LDMxLjUwNCBMMTE4Ljc5NSwyNC41NTEgQzEyMC40NjMsMjMuNTg5IDEyMi42NjksMjMuMDU4IDEyNS4wMDcsMjMuMDU4IEMxMjcuMzI1LDIzLjA1OCAxMjkuNTA2LDIzLjU4MSAxMzEuMTUsMjQuNTMgQzEzMi44NTQsMjUuNTE0IDEzMy43OTMsMjYuODQ1IDEzMy43OTMsMjguMjc4IEMxMzMuNzkzLDI5LjcyNCAxMzIuODQxLDMxLjA2OSAxMzEuMTEzLDMyLjA2NyBMMTE5LjA3MSwzOS4wMTkgQzExNy40MDMsMzkuOTgyIDExNS4xOTcsNDAuNTEyIDExMi44Niw0MC41MTIgTDExMi44Niw0MC41MTIgWiBNMTI1LjAwNywyMy43NTkgQzEyMi43OSwyMy43NTkgMTIwLjcwOSwyNC4yNTYgMTE5LjE0NiwyNS4xNTggTDEwNy4xMDQsMzIuMTEgQzEwNS42MDIsMzIuOTc4IDEwNC43NzQsMzQuMTA4IDEwNC43NzQsMzUuMjkyIEMxMDQuNzc0LDM2LjQ2NSAxMDUuNTg5LDM3LjU4MSAxMDcuMDY3LDM4LjQzNCBDMTA4LjYwNSwzOS4zMjMgMTEwLjY2MywzOS44MTIgMTEyLjg1OSwzOS44MTIgTDExMi44NiwzOS44MTIgQzExNS4wNzYsMzkuODEyIDExNy4xNTgsMzkuMzE1IDExOC43MjEsMzguNDEzIEwxMzAuNzYyLDMxLjQ2IEMxMzIuMjY0LDMwLjU5MyAxMzMuMDkyLDI5LjQ2MyAxMzMuMDkyLDI4LjI3OCBDMTMzLjA5MiwyNy4xMDYgMTMyLjI3OCwyNS45OSAxMzAuOCwyNS4xMzYgQzEyOS4yNjEsMjQuMjQ4IDEyNy4yMDQsMjMuNzU5IDEyNS4wMDcsMjMuNzU5IEwxMjUuMDA3LDIzLjc1OSBaIiBpZD0iRmlsbC0zMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNjUuNjMsMTYuMjE5IEwxNTkuODk2LDE5LjUzIEMxNTYuNzI5LDIxLjM1OCAxNTEuNjEsMjEuMzY3IDE0OC40NjMsMTkuNTUgQzE0NS4zMTYsMTcuNzMzIDE0NS4zMzIsMTQuNzc4IDE0OC40OTksMTIuOTQ5IEwxNTQuMjMzLDkuNjM5IEwxNjUuNjMsMTYuMjE5IiBpZD0iRmlsbC0zMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNTQuMjMzLDEwLjQ0OCBMMTY0LjIyOCwxNi4yMTkgTDE1OS41NDYsMTguOTIzIEMxNTguMTEyLDE5Ljc1IDE1Ni4xOTQsMjAuMjA2IDE1NC4xNDcsMjAuMjA2IEMxNTIuMTE4LDIwLjIwNiAxNTAuMjI0LDE5Ljc1NyAxNDguODE0LDE4Ljk0MyBDMTQ3LjUyNCwxOC4xOTkgMTQ2LjgxNCwxNy4yNDkgMTQ2LjgxNCwxNi4yNjkgQzE0Ni44MTQsMTUuMjc4IDE0Ny41MzcsMTQuMzE0IDE0OC44NSwxMy41NTYgTDE1NC4yMzMsMTAuNDQ4IE0xNTQuMjMzLDkuNjM5IEwxNDguNDk5LDEyLjk0OSBDMTQ1LjMzMiwxNC43NzggMTQ1LjMxNiwxNy43MzMgMTQ4LjQ2MywxOS41NSBDMTUwLjAzMSwyMC40NTUgMTUyLjA4NiwyMC45MDcgMTU0LjE0NywyMC45MDcgQzE1Ni4yMjQsMjAuOTA3IDE1OC4zMDYsMjAuNDQ3IDE1OS44OTYsMTkuNTMgTDE2NS42MywxNi4yMTkgTDE1NC4yMzMsOS42MzkiIGlkPSJGaWxsLTMyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NS40NDUsNzIuNjY3IEwxNDUuNDQ1LDcyLjY2NyBDMTQzLjY3Miw3Mi42NjcgMTQyLjIwNCw3MS44MTcgMTQxLjIwMiw3MC40MjIgQzE0MS4xMzUsNzAuMzMgMTQxLjE0NSw3MC4xNDcgMTQxLjIyNSw3MC4wNjYgQzE0MS4zMDUsNjkuOTg1IDE0MS40MzIsNjkuOTQ2IDE0MS41MjUsNzAuMDExIEMxNDIuMzA2LDcwLjU1OSAxNDMuMjMxLDcwLjgyMyAxNDQuMjc2LDcwLjgyMiBDMTQ1LjU5OCw3MC44MjIgMTQ3LjAzLDcwLjM3NiAxNDguNTMyLDY5LjUwOSBDMTUzLjg0Miw2Ni40NDMgMTU4LjE2Myw1OC45ODcgMTU4LjE2Myw1Mi44OTQgQzE1OC4xNjMsNTAuOTY3IDE1Ny43MjEsNDkuMzMyIDE1Ni44ODQsNDguMTY4IEMxNTYuODE4LDQ4LjA3NiAxNTYuODI4LDQ3Ljk0OCAxNTYuOTA4LDQ3Ljg2NyBDMTU2Ljk4OCw0Ny43ODYgMTU3LjExNCw0Ny43NzQgMTU3LjIwOCw0Ny44NCBDMTU4Ljg3OCw0OS4wMTIgMTU5Ljc5OCw1MS4yMiAxNTkuNzk4LDU0LjA1OSBDMTU5Ljc5OCw2MC4zMDEgMTU1LjM3Myw2OC4wNDYgMTQ5LjkzMyw3MS4xODYgQzE0OC4zNiw3Mi4wOTQgMTQ2Ljg1LDcyLjY2NyAxNDUuNDQ1LDcyLjY2NyBMMTQ1LjQ0NSw3Mi42NjcgWiBNMTQyLjQ3Niw3MSBDMTQzLjI5LDcxLjY1MSAxNDQuMjk2LDcyLjAwMiAxNDUuNDQ1LDcyLjAwMiBDMTQ2Ljc2Nyw3Mi4wMDIgMTQ4LjE5OCw3MS41NSAxNDkuNyw3MC42ODIgQzE1NS4wMSw2Ny42MTcgMTU5LjMzMSw2MC4xNTkgMTU5LjMzMSw1NC4wNjUgQzE1OS4zMzEsNTIuMDg1IDE1OC44NjgsNTAuNDM1IDE1OC4wMDYsNDkuMjcyIEMxNTguNDE3LDUwLjMwNyAxNTguNjMsNTEuNTMyIDE1OC42Myw1Mi44OTIgQzE1OC42Myw1OS4xMzQgMTU0LjIwNSw2Ni43NjcgMTQ4Ljc2NSw2OS45MDcgQzE0Ny4xOTIsNzAuODE2IDE0NS42ODEsNzEuMjgzIDE0NC4yNzYsNzEuMjgzIEMxNDMuNjM0LDcxLjI4MyAxNDMuMDMzLDcxLjE5MiAxNDIuNDc2LDcxIEwxNDIuNDc2LDcxIFoiIGlkPSJGaWxsLTMzIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0OC42NDgsNjkuNzA0IEMxNTQuMDMyLDY2LjU5NiAxNTguMzk2LDU5LjA2OCAxNTguMzk2LDUyLjg5MSBDMTU4LjM5Niw1MC44MzkgMTU3LjkxMyw0OS4xOTggMTU3LjA3NCw0OC4wMyBDMTU1LjI4OSw0Ni43NzggMTUyLjY5OSw0Ni44MzYgMTQ5LjgxNiw0OC41MDEgQzE0NC40MzMsNTEuNjA5IDE0MC4wNjgsNTkuMTM3IDE0MC4wNjgsNjUuMzE0IEMxNDAuMDY4LDY3LjM2NSAxNDAuNTUyLDY5LjAwNiAxNDEuMzkxLDcwLjE3NCBDMTQzLjE3Niw3MS40MjcgMTQ1Ljc2NSw3MS4zNjkgMTQ4LjY0OCw2OS43MDQiIGlkPSJGaWxsLTM0IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NC4yNzYsNzEuMjc2IEwxNDQuMjc2LDcxLjI3NiBDMTQzLjEzMyw3MS4yNzYgMTQyLjExOCw3MC45NjkgMTQxLjI1Nyw3MC4zNjUgQzE0MS4yMzYsNzAuMzUxIDE0MS4yMTcsNzAuMzMyIDE0MS4yMDIsNzAuMzExIEMxNDAuMzA3LDY5LjA2NyAxMzkuODM1LDY3LjMzOSAxMzkuODM1LDY1LjMxNCBDMTM5LjgzNSw1OS4wNzMgMTQ0LjI2LDUxLjQzOSAxNDkuNyw0OC4yOTggQzE1MS4yNzMsNDcuMzkgMTUyLjc4NCw0Ni45MjkgMTU0LjE4OSw0Ni45MjkgQzE1NS4zMzIsNDYuOTI5IDE1Ni4zNDcsNDcuMjM2IDE1Ny4yMDgsNDcuODM5IEMxNTcuMjI5LDQ3Ljg1NCAxNTcuMjQ4LDQ3Ljg3MyAxNTcuMjYzLDQ3Ljg5NCBDMTU4LjE1Nyw0OS4xMzggMTU4LjYzLDUwLjg2NSAxNTguNjMsNTIuODkxIEMxNTguNjMsNTkuMTMyIDE1NC4yMDUsNjYuNzY2IDE0OC43NjUsNjkuOTA3IEMxNDcuMTkyLDcwLjgxNSAxNDUuNjgxLDcxLjI3NiAxNDQuMjc2LDcxLjI3NiBMMTQ0LjI3Niw3MS4yNzYgWiBNMTQxLjU1OCw3MC4xMDQgQzE0Mi4zMzEsNzAuNjM3IDE0My4yNDUsNzEuMDA1IDE0NC4yNzYsNzEuMDA1IEMxNDUuNTk4LDcxLjAwNSAxNDcuMDMsNzAuNDY3IDE0OC41MzIsNjkuNiBDMTUzLjg0Miw2Ni41MzQgMTU4LjE2Myw1OS4wMzMgMTU4LjE2Myw1Mi45MzkgQzE1OC4xNjMsNTEuMDMxIDE1Ny43MjksNDkuMzg1IDE1Ni45MDcsNDguMjIzIEMxNTYuMTMzLDQ3LjY5MSAxNTUuMjE5LDQ3LjQwOSAxNTQuMTg5LDQ3LjQwOSBDMTUyLjg2Nyw0Ny40MDkgMTUxLjQzNSw0Ny44NDIgMTQ5LjkzMyw0OC43MDkgQzE0NC42MjMsNTEuNzc1IDE0MC4zMDIsNTkuMjczIDE0MC4zMDIsNjUuMzY2IEMxNDAuMzAyLDY3LjI3NiAxNDAuNzM2LDY4Ljk0MiAxNDEuNTU4LDcwLjEwNCBMMTQxLjU1OCw3MC4xMDQgWiIgaWQ9IkZpbGwtMzUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUwLjcyLDY1LjM2MSBMMTUwLjM1Nyw2NS4wNjYgQzE1MS4xNDcsNjQuMDkyIDE1MS44NjksNjMuMDQgMTUyLjUwNSw2MS45MzggQzE1My4zMTMsNjAuNTM5IDE1My45NzgsNTkuMDY3IDE1NC40ODIsNTcuNTYzIEwxNTQuOTI1LDU3LjcxMiBDMTU0LjQxMiw1OS4yNDUgMTUzLjczMyw2MC43NDUgMTUyLjkxLDYyLjE3MiBDMTUyLjI2Miw2My4yOTUgMTUxLjUyNSw2NC4zNjggMTUwLjcyLDY1LjM2MSIgaWQ9IkZpbGwtMzYiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE1LjkxNyw4NC41MTQgTDExNS41NTQsODQuMjIgQzExNi4zNDQsODMuMjQ1IDExNy4wNjYsODIuMTk0IDExNy43MDIsODEuMDkyIEMxMTguNTEsNzkuNjkyIDExOS4xNzUsNzguMjIgMTE5LjY3OCw3Ni43MTcgTDEyMC4xMjEsNzYuODY1IEMxMTkuNjA4LDc4LjM5OCAxMTguOTMsNzkuODk5IDExOC4xMDYsODEuMzI2IEMxMTcuNDU4LDgyLjQ0OCAxMTYuNzIyLDgzLjUyMSAxMTUuOTE3LDg0LjUxNCIgaWQ9IkZpbGwtMzciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE0LDEzMC40NzYgTDExNCwxMzAuMDA4IEwxMTQsNzYuMDUyIEwxMTQsNzUuNTg0IEwxMTQsNzYuMDUyIEwxMTQsMTMwLjAwOCBMMTE0LDEzMC40NzYiIGlkPSJGaWxsLTM4IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYyLjAwMDAwMCwgMC4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTkuODIyLDM3LjQ3NCBDMTkuODM5LDM3LjMzOSAxOS43NDcsMzcuMTk0IDE5LjU1NSwzNy4wODIgQzE5LjIyOCwzNi44OTQgMTguNzI5LDM2Ljg3MiAxOC40NDYsMzcuMDM3IEwxMi40MzQsNDAuNTA4IEMxMi4zMDMsNDAuNTg0IDEyLjI0LDQwLjY4NiAxMi4yNDMsNDAuNzkzIEMxMi4yNDUsNDAuOTI1IDEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQxLjM3MSBMMTIuMjQ1LDQxLjQxNCBMMTIuMjM4LDQxLjU0MiBDOC4xNDgsNDMuODg3IDUuNjQ3LDQ1LjMyMSA1LjY0Nyw0NS4zMjEgQzUuNjQ2LDQ1LjMyMSAzLjU3LDQ2LjM2NyAyLjg2LDUwLjUxMyBDMi44Niw1MC41MTMgMS45NDgsNTcuNDc0IDEuOTYyLDcwLjI1OCBDMS45NzcsODIuODI4IDIuNTY4LDg3LjMyOCAzLjEyOSw5MS42MDkgQzMuMzQ5LDkzLjI5MyA2LjEzLDkzLjczNCA2LjEzLDkzLjczNCBDNi40NjEsOTMuNzc0IDYuODI4LDkzLjcwNyA3LjIxLDkzLjQ4NiBMODIuNDgzLDQ5LjkzNSBDODQuMjkxLDQ4Ljg2NiA4NS4xNSw0Ni4yMTYgODUuNTM5LDQzLjY1MSBDODYuNzUyLDM1LjY2MSA4Ny4yMTQsMTAuNjczIDg1LjI2NCwzLjc3MyBDODUuMDY4LDMuMDggODQuNzU0LDIuNjkgODQuMzk2LDIuNDkxIEw4Mi4zMSwxLjcwMSBDODEuNTgzLDEuNzI5IDgwLjg5NCwyLjE2OCA4MC43NzYsMi4yMzYgQzgwLjYzNiwyLjMxNyA0MS44MDcsMjQuNTg1IDIwLjAzMiwzNy4wNzIgTDE5LjgyMiwzNy40NzQiIGlkPSJGaWxsLTEiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNODIuMzExLDEuNzAxIEw4NC4zOTYsMi40OTEgQzg0Ljc1NCwyLjY5IDg1LjA2OCwzLjA4IDg1LjI2NCwzLjc3MyBDODcuMjEzLDEwLjY3MyA4Ni43NTEsMzUuNjYgODUuNTM5LDQzLjY1MSBDODUuMTQ5LDQ2LjIxNiA4NC4yOSw0OC44NjYgODIuNDgzLDQ5LjkzNSBMNy4yMSw5My40ODYgQzYuODk3LDkzLjY2NyA2LjU5NSw5My43NDQgNi4zMTQsOTMuNzQ0IEw2LjEzMSw5My43MzMgQzYuMTMxLDkzLjczNCAzLjM0OSw5My4yOTMgMy4xMjgsOTEuNjA5IEMyLjU2OCw4Ny4zMjcgMS45NzcsODIuODI4IDEuOTYzLDcwLjI1OCBDMS45NDgsNTcuNDc0IDIuODYsNTAuNTEzIDIuODYsNTAuNTEzIEMzLjU3LDQ2LjM2NyA1LjY0Nyw0NS4zMjEgNS42NDcsNDUuMzIxIEM1LjY0Nyw0NS4zMjEgOC4xNDgsNDMuODg3IDEyLjIzOCw0MS41NDIgTDEyLjI0NSw0MS40MTQgTDEyLjI0NSw0MS4zNzEgQzEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQwLjkyNSAxMi4yNDMsNDAuNzkzIEMxMi4yNCw0MC42ODYgMTIuMzAyLDQwLjU4MyAxMi40MzQsNDAuNTA4IEwxOC40NDYsMzcuMDM2IEMxOC41NzQsMzYuOTYyIDE4Ljc0NiwzNi45MjYgMTguOTI3LDM2LjkyNiBDMTkuMTQ1LDM2LjkyNiAxOS4zNzYsMzYuOTc5IDE5LjU1NCwzNy4wODIgQzE5Ljc0NywzNy4xOTQgMTkuODM5LDM3LjM0IDE5LjgyMiwzNy40NzQgTDIwLjAzMywzNy4wNzIgQzQxLjgwNiwyNC41ODUgODAuNjM2LDIuMzE4IDgwLjc3NywyLjIzNiBDODAuODk0LDIuMTY4IDgxLjU4MywxLjcyOSA4Mi4zMTEsMS43MDEgTTgyLjMxMSwwLjcwNCBMODIuMjcyLDAuNzA1IEM4MS42NTQsMC43MjggODAuOTg5LDAuOTQ5IDgwLjI5OCwxLjM2MSBMODAuMjc3LDEuMzczIEM4MC4xMjksMS40NTggNTkuNzY4LDEzLjEzNSAxOS43NTgsMzYuMDc5IEMxOS41LDM1Ljk4MSAxOS4yMTQsMzUuOTI5IDE4LjkyNywzNS45MjkgQzE4LjU2MiwzNS45MjkgMTguMjIzLDM2LjAxMyAxNy45NDcsMzYuMTczIEwxMS45MzUsMzkuNjQ0IEMxMS40OTMsMzkuODk5IDExLjIzNiw0MC4zMzQgMTEuMjQ2LDQwLjgxIEwxMS4yNDcsNDAuOTYgTDUuMTY3LDQ0LjQ0NyBDNC43OTQsNDQuNjQ2IDIuNjI1LDQ1Ljk3OCAxLjg3Nyw1MC4zNDUgTDEuODcxLDUwLjM4NCBDMS44NjIsNTAuNDU0IDAuOTUxLDU3LjU1NyAwLjk2NSw3MC4yNTkgQzAuOTc5LDgyLjg3OSAxLjU2OCw4Ny4zNzUgMi4xMzcsOTEuNzI0IEwyLjEzOSw5MS43MzkgQzIuNDQ3LDk0LjA5NCA1LjYxNCw5NC42NjIgNS45NzUsOTQuNzE5IEw2LjAwOSw5NC43MjMgQzYuMTEsOTQuNzM2IDYuMjEzLDk0Ljc0MiA2LjMxNCw5NC43NDIgQzYuNzksOTQuNzQyIDcuMjYsOTQuNjEgNy43MSw5NC4zNSBMODIuOTgzLDUwLjc5OCBDODQuNzk0LDQ5LjcyNyA4NS45ODIsNDcuMzc1IDg2LjUyNSw0My44MDEgQzg3LjcxMSwzNS45ODcgODguMjU5LDEwLjcwNSA4Ni4yMjQsMy41MDIgQzg1Ljk3MSwyLjYwOSA4NS41MiwxLjk3NSA4NC44ODEsMS42MiBMODQuNzQ5LDEuNTU4IEw4Mi42NjQsMC43NjkgQzgyLjU1MSwwLjcyNSA4Mi40MzEsMC43MDQgODIuMzExLDAuNzA0IiBpZD0iRmlsbC0yIiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY2LjI2NywxMS41NjUgTDY3Ljc2MiwxMS45OTkgTDExLjQyMyw0NC4zMjUiIGlkPSJGaWxsLTMiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMjAyLDkwLjU0NSBDMTIuMDI5LDkwLjU0NSAxMS44NjIsOTAuNDU1IDExLjc2OSw5MC4yOTUgQzExLjYzMiw5MC4wNTcgMTEuNzEzLDg5Ljc1MiAxMS45NTIsODkuNjE0IEwzMC4zODksNzguOTY5IEMzMC42MjgsNzguODMxIDMwLjkzMyw3OC45MTMgMzEuMDcxLDc5LjE1MiBDMzEuMjA4LDc5LjM5IDMxLjEyNyw3OS42OTYgMzAuODg4LDc5LjgzMyBMMTIuNDUxLDkwLjQ3OCBMMTIuMjAyLDkwLjU0NSIgaWQ9IkZpbGwtNCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMy43NjQsNDIuNjU0IEwxMy42NTYsNDIuNTkyIEwxMy43MDIsNDIuNDIxIEwxOC44MzcsMzkuNDU3IEwxOS4wMDcsMzkuNTAyIEwxOC45NjIsMzkuNjczIEwxMy44MjcsNDIuNjM3IEwxMy43NjQsNDIuNjU0IiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTguNTIsOTAuMzc1IEw4LjUyLDQ2LjQyMSBMOC41ODMsNDYuMzg1IEw3NS44NCw3LjU1NCBMNzUuODQsNTEuNTA4IEw3NS43NzgsNTEuNTQ0IEw4LjUyLDkwLjM3NSBMOC41Miw5MC4zNzUgWiBNOC43Nyw0Ni41NjQgTDguNzcsODkuOTQ0IEw3NS41OTEsNTEuMzY1IEw3NS41OTEsNy45ODUgTDguNzcsNDYuNTY0IEw4Ljc3LDQ2LjU2NCBaIiBpZD0iRmlsbC02IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTI0Ljk4Niw4My4xODIgQzI0Ljc1Niw4My4zMzEgMjQuMzc0LDgzLjU2NiAyNC4xMzcsODMuNzA1IEwxMi42MzIsOTAuNDA2IEMxMi4zOTUsOTAuNTQ1IDEyLjQyNiw5MC42NTggMTIuNyw5MC42NTggTDEzLjI2NSw5MC42NTggQzEzLjU0LDkwLjY1OCAxMy45NTgsOTAuNTQ1IDE0LjE5NSw5MC40MDYgTDI1LjcsODMuNzA1IEMyNS45MzcsODMuNTY2IDI2LjEyOCw4My40NTIgMjYuMTI1LDgzLjQ0OSBDMjYuMTIyLDgzLjQ0NyAyNi4xMTksODMuMjIgMjYuMTE5LDgyLjk0NiBDMjYuMTE5LDgyLjY3MiAyNS45MzEsODIuNTY5IDI1LjcwMSw4Mi43MTkgTDI0Ljk4Niw4My4xODIiIGlkPSJGaWxsLTciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTMuMjY2LDkwLjc4MiBMMTIuNyw5MC43ODIgQzEyLjUsOTAuNzgyIDEyLjM4NCw5MC43MjYgMTIuMzU0LDkwLjYxNiBDMTIuMzI0LDkwLjUwNiAxMi4zOTcsOTAuMzk5IDEyLjU2OSw5MC4yOTkgTDI0LjA3NCw4My41OTcgQzI0LjMxLDgzLjQ1OSAyNC42ODksODMuMjI2IDI0LjkxOCw4My4wNzggTDI1LjYzMyw4Mi42MTQgQzI1LjcyMyw4Mi41NTUgMjUuODEzLDgyLjUyNSAyNS44OTksODIuNTI1IEMyNi4wNzEsODIuNTI1IDI2LjI0NCw4Mi42NTUgMjYuMjQ0LDgyLjk0NiBDMjYuMjQ0LDgzLjE2IDI2LjI0NSw4My4zMDkgMjYuMjQ3LDgzLjM4MyBMMjYuMjUzLDgzLjM4NyBMMjYuMjQ5LDgzLjQ1NiBDMjYuMjQ2LDgzLjUzMSAyNi4yNDYsODMuNTMxIDI1Ljc2Myw4My44MTIgTDE0LjI1OCw5MC41MTQgQzE0LDkwLjY2NSAxMy41NjQsOTAuNzgyIDEzLjI2Niw5MC43ODIgTDEzLjI2Niw5MC43ODIgWiBNMTIuNjY2LDkwLjUzMiBMMTIuNyw5MC41MzMgTDEzLjI2Niw5MC41MzMgQzEzLjUxOCw5MC41MzMgMTMuOTE1LDkwLjQyNSAxNC4xMzIsOTAuMjk5IEwyNS42MzcsODMuNTk3IEMyNS44MDUsODMuNDk5IDI1LjkzMSw4My40MjQgMjUuOTk4LDgzLjM4MyBDMjUuOTk0LDgzLjI5OSAyNS45OTQsODMuMTY1IDI1Ljk5NCw4Mi45NDYgTDI1Ljg5OSw4Mi43NzUgTDI1Ljc2OCw4Mi44MjQgTDI1LjA1NCw4My4yODcgQzI0LjgyMiw4My40MzcgMjQuNDM4LDgzLjY3MyAyNC4yLDgzLjgxMiBMMTIuNjk1LDkwLjUxNCBMMTIuNjY2LDkwLjUzMiBMMTIuNjY2LDkwLjUzMiBaIiBpZD0iRmlsbC04IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEzLjI2Niw4OS44NzEgTDEyLjcsODkuODcxIEMxMi41LDg5Ljg3MSAxMi4zODQsODkuODE1IDEyLjM1NCw4OS43MDUgQzEyLjMyNCw4OS41OTUgMTIuMzk3LDg5LjQ4OCAxMi41NjksODkuMzg4IEwyNC4wNzQsODIuNjg2IEMyNC4zMzIsODIuNTM1IDI0Ljc2OCw4Mi40MTggMjUuMDY3LDgyLjQxOCBMMjUuNjMyLDgyLjQxOCBDMjUuODMyLDgyLjQxOCAyNS45NDgsODIuNDc0IDI1Ljk3OCw4Mi41ODQgQzI2LjAwOCw4Mi42OTQgMjUuOTM1LDgyLjgwMSAyNS43NjMsODIuOTAxIEwxNC4yNTgsODkuNjAzIEMxNCw4OS43NTQgMTMuNTY0LDg5Ljg3MSAxMy4yNjYsODkuODcxIEwxMy4yNjYsODkuODcxIFogTTEyLjY2Niw4OS42MjEgTDEyLjcsODkuNjIyIEwxMy4yNjYsODkuNjIyIEMxMy41MTgsODkuNjIyIDEzLjkxNSw4OS41MTUgMTQuMTMyLDg5LjM4OCBMMjUuNjM3LDgyLjY4NiBMMjUuNjY3LDgyLjY2OCBMMjUuNjMyLDgyLjY2NyBMMjUuMDY3LDgyLjY2NyBDMjQuODE1LDgyLjY2NyAyNC40MTgsODIuNzc1IDI0LjIsODIuOTAxIEwxMi42OTUsODkuNjAzIEwxMi42NjYsODkuNjIxIEwxMi42NjYsODkuNjIxIFoiIGlkPSJGaWxsLTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMzcsOTAuODAxIEwxMi4zNyw4OS41NTQgTDEyLjM3LDkwLjgwMSIgaWQ9IkZpbGwtMTAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNi4xMyw5My45MDEgQzUuMzc5LDkzLjgwOCA0LjgxNiw5My4xNjQgNC42OTEsOTIuNTI1IEMzLjg2LDg4LjI4NyAzLjU0LDgzLjc0MyAzLjUyNiw3MS4xNzMgQzMuNTExLDU4LjM4OSA0LjQyMyw1MS40MjggNC40MjMsNTEuNDI4IEM1LjEzNCw0Ny4yODIgNy4yMSw0Ni4yMzYgNy4yMSw0Ni4yMzYgQzcuMjEsNDYuMjM2IDgxLjY2NywzLjI1IDgyLjA2OSwzLjAxNyBDODIuMjkyLDIuODg4IDg0LjU1NiwxLjQzMyA4NS4yNjQsMy45NCBDODcuMjE0LDEwLjg0IDg2Ljc1MiwzNS44MjcgODUuNTM5LDQzLjgxOCBDODUuMTUsNDYuMzgzIDg0LjI5MSw0OS4wMzMgODIuNDgzLDUwLjEwMSBMNy4yMSw5My42NTMgQzYuODI4LDkzLjg3NCA2LjQ2MSw5My45NDEgNi4xMyw5My45MDEgQzYuMTMsOTMuOTAxIDMuMzQ5LDkzLjQ2IDMuMTI5LDkxLjc3NiBDMi41NjgsODcuNDk1IDEuOTc3LDgyLjk5NSAxLjk2Miw3MC40MjUgQzEuOTQ4LDU3LjY0MSAyLjg2LDUwLjY4IDIuODYsNTAuNjggQzMuNTcsNDYuNTM0IDUuNjQ3LDQ1LjQ4OSA1LjY0Nyw0NS40ODkgQzUuNjQ2LDQ1LjQ4OSA4LjA2NSw0NC4wOTIgMTIuMjQ1LDQxLjY3OSBMMTMuMTE2LDQxLjU2IEwxOS43MTUsMzcuNzMgTDE5Ljc2MSwzNy4yNjkgTDYuMTMsOTMuOTAxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjMxNyw5NC4xNjEgTDYuMTAyLDk0LjE0OCBMNi4xMDEsOTQuMTQ4IEw1Ljg1Nyw5NC4xMDEgQzUuMTM4LDkzLjk0NSAzLjA4NSw5My4zNjUgMi44ODEsOTEuODA5IEMyLjMxMyw4Ny40NjkgMS43MjcsODIuOTk2IDEuNzEzLDcwLjQyNSBDMS42OTksNTcuNzcxIDIuNjA0LDUwLjcxOCAyLjYxMyw1MC42NDggQzMuMzM4LDQ2LjQxNyA1LjQ0NSw0NS4zMSA1LjUzNSw0NS4yNjYgTDEyLjE2Myw0MS40MzkgTDEzLjAzMyw0MS4zMiBMMTkuNDc5LDM3LjU3OCBMMTkuNTEzLDM3LjI0NCBDMTkuNTI2LDM3LjEwNyAxOS42NDcsMzcuMDA4IDE5Ljc4NiwzNy4wMjEgQzE5LjkyMiwzNy4wMzQgMjAuMDIzLDM3LjE1NiAyMC4wMDksMzcuMjkzIEwxOS45NSwzNy44ODIgTDEzLjE5OCw0MS44MDEgTDEyLjMyOCw0MS45MTkgTDUuNzcyLDQ1LjcwNCBDNS43NDEsNDUuNzIgMy43ODIsNDYuNzcyIDMuMTA2LDUwLjcyMiBDMy4wOTksNTAuNzgyIDIuMTk4LDU3LjgwOCAyLjIxMiw3MC40MjQgQzIuMjI2LDgyLjk2MyAyLjgwOSw4Ny40MiAzLjM3Myw5MS43MjkgQzMuNDY0LDkyLjQyIDQuMDYyLDkyLjg4MyA0LjY4Miw5My4xODEgQzQuNTY2LDkyLjk4NCA0LjQ4Niw5Mi43NzYgNC40NDYsOTIuNTcyIEMzLjY2NSw4OC41ODggMy4yOTEsODQuMzcgMy4yNzYsNzEuMTczIEMzLjI2Miw1OC41MiA0LjE2Nyw1MS40NjYgNC4xNzYsNTEuMzk2IEM0LjkwMSw0Ny4xNjUgNy4wMDgsNDYuMDU5IDcuMDk4LDQ2LjAxNCBDNy4wOTQsNDYuMDE1IDgxLjU0MiwzLjAzNCA4MS45NDQsMi44MDIgTDgxLjk3MiwyLjc4NSBDODIuODc2LDIuMjQ3IDgzLjY5MiwyLjA5NyA4NC4zMzIsMi4zNTIgQzg0Ljg4NywyLjU3MyA4NS4yODEsMy4wODUgODUuNTA0LDMuODcyIEM4Ny41MTgsMTEgODYuOTY0LDM2LjA5MSA4NS43ODUsNDMuODU1IEM4NS4yNzgsNDcuMTk2IDg0LjIxLDQ5LjM3IDgyLjYxLDUwLjMxNyBMNy4zMzUsOTMuODY5IEM2Ljk5OSw5NC4wNjMgNi42NTgsOTQuMTYxIDYuMzE3LDk0LjE2MSBMNi4zMTcsOTQuMTYxIFogTTYuMTcsOTMuNjU0IEM2LjQ2Myw5My42OSA2Ljc3NCw5My42MTcgNy4wODUsOTMuNDM3IEw4Mi4zNTgsNDkuODg2IEM4NC4xODEsNDguODA4IDg0Ljk2LDQ1Ljk3MSA4NS4yOTIsNDMuNzggQzg2LjQ2NiwzNi4wNDkgODcuMDIzLDExLjA4NSA4NS4wMjQsNC4wMDggQzg0Ljg0NiwzLjM3NyA4NC41NTEsMi45NzYgODQuMTQ4LDIuODE2IEM4My42NjQsMi42MjMgODIuOTgyLDIuNzY0IDgyLjIyNywzLjIxMyBMODIuMTkzLDMuMjM0IEM4MS43OTEsMy40NjYgNy4zMzUsNDYuNDUyIDcuMzM1LDQ2LjQ1MiBDNy4zMDQsNDYuNDY5IDUuMzQ2LDQ3LjUyMSA0LjY2OSw1MS40NzEgQzQuNjYyLDUxLjUzIDMuNzYxLDU4LjU1NiAzLjc3NSw3MS4xNzMgQzMuNzksODQuMzI4IDQuMTYxLDg4LjUyNCA0LjkzNiw5Mi40NzYgQzUuMDI2LDkyLjkzNyA1LjQxMiw5My40NTkgNS45NzMsOTMuNjE1IEM2LjA4Nyw5My42NCA2LjE1OCw5My42NTIgNi4xNjksOTMuNjU0IEw2LjE3LDkzLjY1NCBMNi4xNyw5My42NTQgWiIgaWQ9IkZpbGwtMTIiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4zMTcsNjguOTgyIEM3LjgwNiw2OC43MDEgOC4yMDIsNjguOTI2IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNi44MjksNzEuMjk0IDYuNDMzLDcxLjA2OSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIiBpZD0iRmlsbC0xMyIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjkyLDcxLjEzMyBDNi42MzEsNzEuMTMzIDYuNDMzLDcwLjkwNSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIEM3LjQ2LDY4LjkgNy41OTUsNjguODYxIDcuNzE0LDY4Ljg2MSBDOC4wMDMsNjguODYxIDguMjAyLDY5LjA5IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNy4xNzQsNzEuMDk0IDcuMDM5LDcxLjEzMyA2LjkyLDcxLjEzMyBNNy43MTQsNjguNjc0IEM3LjU1Nyw2OC42NzQgNy4zOTIsNjguNzIzIDcuMjI0LDY4LjgyMSBDNi42NzYsNjkuMTM4IDYuMjQ2LDY5Ljg3OSA2LjI0Niw3MC41MDggQzYuMjQ2LDcwLjk5NCA2LjUxNyw3MS4zMiA2LjkyLDcxLjMyIEM3LjA3OCw3MS4zMiA3LjI0Myw3MS4yNzEgNy40MTEsNzEuMTc0IEM3Ljk1OSw3MC44NTcgOC4zODksNzAuMTE3IDguMzg5LDY5LjQ4NyBDOC4zODksNjkuMDAxIDguMTE3LDY4LjY3NCA3LjcxNCw2OC42NzQiIGlkPSJGaWxsLTE0IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYuOTIsNzAuOTQ3IEM2LjY0OSw3MC45NDcgNi42MjEsNzAuNjQgNi42MjEsNzAuNTA4IEM2LjYyMSw3MC4wMTcgNi45ODIsNjkuMzkyIDcuNDExLDY5LjE0NSBDNy41MjEsNjkuMDgyIDcuNjI1LDY5LjA0OSA3LjcxNCw2OS4wNDkgQzcuOTg2LDY5LjA0OSA4LjAxNSw2OS4zNTUgOC4wMTUsNjkuNDg3IEM4LjAxNSw2OS45NzggNy42NTIsNzAuNjAzIDcuMjI0LDcwLjg1MSBDNy4xMTUsNzAuOTE0IDcuMDEsNzAuOTQ3IDYuOTIsNzAuOTQ3IE03LjcxNCw2OC44NjEgQzcuNTk1LDY4Ljg2MSA3LjQ2LDY4LjkgNy4zMTcsNjguOTgyIEM2LjgyOSw2OS4yNjUgNi40MzMsNjkuOTQ4IDYuNDMzLDcwLjUwOCBDNi40MzMsNzAuOTA1IDYuNjMxLDcxLjEzMyA2LjkyLDcxLjEzMyBDNy4wMzksNzEuMTMzIDcuMTc0LDcxLjA5NCA3LjMxNyw3MS4wMTIgQzcuODA2LDcwLjczIDguMjAyLDcwLjA0NyA4LjIwMiw2OS40ODcgQzguMjAyLDY5LjA5IDguMDAzLDY4Ljg2MSA3LjcxNCw2OC44NjEiIGlkPSJGaWxsLTE1IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTcuNDQ0LDg1LjM1IEM3LjcwOCw4NS4xOTggNy45MjEsODUuMzE5IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuOTI1IDcuNzA4LDg2LjI5MiA3LjQ0NCw4Ni40NDQgQzcuMTgxLDg2LjU5NyA2Ljk2Nyw4Ni40NzUgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IiBpZD0iRmlsbC0xNiIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik03LjIzLDg2LjUxIEM3LjA3NCw4Ni41MSA2Ljk2Nyw4Ni4zODcgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IEM3LjUyMSw4NS4zMDUgNy41OTQsODUuMjg0IDcuNjU4LDg1LjI4NCBDNy44MTQsODUuMjg0IDcuOTIxLDg1LjQwOCA3LjkyMSw4NS42MjIgQzcuOTIxLDg1LjkyNSA3LjcwOCw4Ni4yOTIgNy40NDQsODYuNDQ0IEM3LjM2Nyw4Ni40ODkgNy4yOTQsODYuNTEgNy4yMyw4Ni41MSBNNy42NTgsODUuMDk4IEM3LjU1OCw4NS4wOTggNy40NTUsODUuMTI3IDcuMzUxLDg1LjE4OCBDNy4wMzEsODUuMzczIDYuNzgxLDg1LjgwNiA2Ljc4MSw4Ni4xNzMgQzYuNzgxLDg2LjQ4MiA2Ljk2Niw4Ni42OTcgNy4yMyw4Ni42OTcgQzcuMzMsODYuNjk3IDcuNDMzLDg2LjY2NiA3LjUzOCw4Ni42MDcgQzcuODU4LDg2LjQyMiA4LjEwOCw4NS45ODkgOC4xMDgsODUuNjIyIEM4LjEwOCw4NS4zMTMgNy45MjMsODUuMDk4IDcuNjU4LDg1LjA5OCIgaWQ9IkZpbGwtMTciIGZpbGw9IiM4MDk3QTIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4yMyw4Ni4zMjIgTDcuMTU0LDg2LjE3MyBDNy4xNTQsODUuOTM4IDcuMzMzLDg1LjYyOSA3LjUzOCw4NS41MTIgTDcuNjU4LDg1LjQ3MSBMNy43MzQsODUuNjIyIEM3LjczNCw4NS44NTYgNy41NTUsODYuMTY0IDcuMzUxLDg2LjI4MiBMNy4yMyw4Ni4zMjIgTTcuNjU4LDg1LjI4NCBDNy41OTQsODUuMjg0IDcuNTIxLDg1LjMwNSA3LjQ0NCw4NS4zNSBDNy4xODEsODUuNTAyIDYuOTY3LDg1Ljg3MSA2Ljk2Nyw4Ni4xNzMgQzYuOTY3LDg2LjM4NyA3LjA3NCw4Ni41MSA3LjIzLDg2LjUxIEM3LjI5NCw4Ni41MSA3LjM2Nyw4Ni40ODkgNy40NDQsODYuNDQ0IEM3LjcwOCw4Ni4yOTIgNy45MjEsODUuOTI1IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuNDA4IDcuODE0LDg1LjI4NCA3LjY1OCw4NS4yODQiIGlkPSJGaWxsLTE4IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTc3LjI3OCw3Ljc2OSBMNzcuMjc4LDUxLjQzNiBMMTAuMjA4LDkwLjE2IEwxMC4yMDgsNDYuNDkzIEw3Ny4yNzgsNy43NjkiIGlkPSJGaWxsLTE5IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEwLjA4Myw5MC4zNzUgTDEwLjA4Myw0Ni40MjEgTDEwLjE0Niw0Ni4zODUgTDc3LjQwMyw3LjU1NCBMNzcuNDAzLDUxLjUwOCBMNzcuMzQxLDUxLjU0NCBMMTAuMDgzLDkwLjM3NSBMMTAuMDgzLDkwLjM3NSBaIE0xMC4zMzMsNDYuNTY0IEwxMC4zMzMsODkuOTQ0IEw3Ny4xNTQsNTEuMzY1IEw3Ny4xNTQsNy45ODUgTDEwLjMzMyw0Ni41NjQgTDEwLjMzMyw0Ni41NjQgWiIgaWQ9IkZpbGwtMjAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMjUuNzM3LDg4LjY0NyBMMTE4LjA5OCw5MS45ODEgTDExOC4wOTgsODQgTDEwNi42MzksODguNzEzIEwxMDYuNjM5LDk2Ljk4MiBMOTksMTAwLjMxNSBMMTEyLjM2OSwxMDMuOTYxIEwxMjUuNzM3LDg4LjY0NyIgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTIiIGZpbGw9IiM0NTVBNjQiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+');
+};
+
+module.exports = RotateInstructions;
+
+},{"./util.js":13}],13:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var Util = {};
+
+Util.base64 = function(mimeType, base64) {
+ return 'data:' + mimeType + ';base64,' + base64;
+};
+
+Util.isMobile = function() {
+ var check = false;
+ (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
+ return check;
+};
+
+Util.isFirefox = function() {
+ return /firefox/i.test(navigator.userAgent);
+};
+
+Util.isIOS = function() {
+ return /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
+};
+
+Util.isIFrame = function() {
+ try {
+ return window.self !== window.top;
+ } catch (e) {
+ return true;
+ }
+};
+
+Util.appendQueryParameter = function(url, key, value) {
+ // Determine delimiter based on if the URL already GET parameters in it.
+ var delimiter = (url.indexOf('?') < 0 ? '?' : '&');
+ url += delimiter + key + '=' + value;
+ return url;
+};
+
+// From http://goo.gl/4WX3tg
+Util.getQueryParameter = function(name) {
+ name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
+ var regex = new RegExp("[\\?&]" + name + "=([^]*)"),
+ results = regex.exec(location.search);
+ return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
+};
+
+Util.isLandscapeMode = function() {
+ return (window.orientation == 90 || window.orientation == -90);
+};
+
+Util.getScreenWidth = function() {
+ return Math.max(window.screen.width, window.screen.height) *
+ window.devicePixelRatio;
+};
+
+Util.getScreenHeight = function() {
+ return Math.min(window.screen.width, window.screen.height) *
+ window.devicePixelRatio;
+};
+
+/**
+ * Utility to convert the projection matrix to a vector accepted by the shader.
+ *
+ * @param {Object} opt_params A rectangle to scale this vector by.
+ */
+Util.projectionMatrixToVector_ = function(matrix, opt_params) {
+ var params = opt_params || {};
+ var xScale = params.xScale || 1;
+ var yScale = params.yScale || 1;
+ var xTrans = params.xTrans || 0;
+ var yTrans = params.yTrans || 0;
+
+ var elements = matrix.elements;
+ var vec = new THREE.Vector4();
+ vec.set(elements[4*0 + 0] * xScale,
+ elements[4*1 + 1] * yScale,
+ elements[4*2 + 0] - 1 - xTrans,
+ elements[4*2 + 1] - 1 - yTrans).divideScalar(2);
+ return vec;
+};
+
+Util.leftProjectionVectorToRight_ = function(left) {
+ //projectionLeft + vec4(0.0, 0.0, 1.0, 0.0)) * vec4(1.0, 1.0, -1.0, 1.0);
+ var out = new THREE.Vector4(0, 0, 1, 0);
+ out.add(left); // out = left + (0, 0, 1, 0).
+ out.z *= -1; // Flip z.
+
+ return out;
+};
+
+module.exports = Util;
+
+},{}],14:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var Emitter = _dereq_('./emitter.js');
+var Util = _dereq_('./util.js');
+
+var DEFAULT_VIEWER = 'CardboardV1';
+var VIEWER_KEY = 'WEBVR_CARDBOARD_VIEWER';
+
+/**
+ * Creates a viewer selector with the options specified. Supports being shown
+ * and hidden. Generates events when viewer parameters change. Also supports
+ * saving the currently selected index in localStorage.
+ *
+ * @param {Object} options Option labels for all valid selections {name: index}.
+ */
+function ViewerSelector(options) {
+ // Try to load the selected key from local storage. If none exists, use the
+ // default key.
+ try {
+ this.selectedKey = localStorage.getItem(VIEWER_KEY) || DEFAULT_VIEWER;
+ } catch(error) {
+ console.error('Failed to load viewer profile: %s', error);
+ }
+ this.dialog = this.createDialog_(options);
+ this.options = options;
+ document.body.appendChild(this.dialog);
+}
+ViewerSelector.prototype = new Emitter();
+
+ViewerSelector.prototype.show = function() {
+ //console.log('ViewerSelector.show');
+
+ // Ensure the currently selected item is checked.
+ var selected = this.dialog.querySelector('#' + this.selectedKey);
+ selected.checked = true;
+
+ // Show the UI.
+ this.dialog.style.display = 'block';
+};
+
+ViewerSelector.prototype.hide = function() {
+ //console.log('ViewerSelector.hide');
+ this.dialog.style.display = 'none';
+};
+
+ViewerSelector.prototype.getSelectedKey_ = function() {
+ var input = this.dialog.querySelector('input[name=field]:checked');
+ if (input) {
+ return input.id;
+ }
+ return null;
+};
+
+ViewerSelector.prototype.onSave_ = function() {
+ this.selectedKey = this.getSelectedKey_();
+ if (!this.selectedKey || !this.options[this.selectedKey]) {
+ console.error('ViewerSelector.onSave_: this should never happen!');
+ return;
+ }
+
+ this.emit('change', this.options[this.selectedKey]);
+
+ // Attempt to save the viewer profile, but fails in private mode.
+ try {
+ localStorage.setItem(VIEWER_KEY, this.selectedKey);
+ } catch(error) {
+ console.error('Failed to save viewer profile: %s', error);
+ }
+ this.hide();
+};
+
+/**
+ * Creates the dialog.
+ */
+ViewerSelector.prototype.createDialog_ = function(options) {
+ var container = document.createElement('div');
+ container.style.display = 'none';
+ // Create an overlay that dims the background, and which goes away when you
+ // tap it.
+ var overlay = document.createElement('div');
+ var s = overlay.style;
+ s.position = 'fixed';
+ s.left = 0;
+ s.top = 0;
+ s.width = '100%';
+ s.height = '100%';
+ s.background = 'rgba(0, 0, 0, 0.3)';
+ overlay.addEventListener('click', this.hide.bind(this));
+
+ var width = 280;
+ var dialog = document.createElement('div');
+ var s = dialog.style;
+ s.boxSizing = 'border-box';
+ s.position = 'fixed';
+ s.top = '24px';
+ s.left = '50%';
+ s.marginLeft = (-width/2) + 'px';
+ s.width = width + 'px';
+ s.padding = '24px';
+ s.overflow = 'hidden';
+ s.background = '#fafafa';
+ s.fontFamily = "'Roboto', sans-serif";
+ s.boxShadow = '0px 5px 20px #666';
+
+ dialog.appendChild(this.createH1_('Select your viewer'));
+ for (var id in options) {
+ dialog.appendChild(this.createChoice_(id, options[id].label));
+ }
+ dialog.appendChild(this.createButton_('Save', this.onSave_.bind(this)));
+
+ container.appendChild(overlay);
+ container.appendChild(dialog);
+
+ return container;
+};
+
+ViewerSelector.prototype.createH1_ = function(name) {
+ var h1 = document.createElement('h1');
+ var s = h1.style;
+ s.color = 'black';
+ s.fontSize = '20px';
+ s.fontWeight = 'bold';
+ s.marginTop = 0;
+ s.marginBottom = '24px';
+ h1.innerHTML = name;
+ return h1;
+};
+
+ViewerSelector.prototype.createChoice_ = function(id, name) {
+ /*
+
+
+
+
+ */
+ var div = document.createElement('div');
+ div.style.marginTop = '8px';
+ div.style.color = 'black';
+
+ var input = document.createElement('input');
+ input.style.fontSize = '30px';
+ input.setAttribute('id', id);
+ input.setAttribute('type', 'radio');
+ input.setAttribute('value', id);
+ input.setAttribute('name', 'field');
+
+ var label = document.createElement('label');
+ label.style.marginLeft = '4px';
+ label.setAttribute('for', id);
+ label.innerHTML = name;
+
+ div.appendChild(input);
+ div.appendChild(label);
+
+ return div;
+};
+
+ViewerSelector.prototype.createButton_ = function(label, onclick) {
+ var button = document.createElement('button');
+ button.innerHTML = label;
+ var s = button.style;
+ s.float = 'right';
+ s.textTransform = 'uppercase';
+ s.color = '#1094f7';
+ s.fontSize = '14px';
+ s.letterSpacing = 0;
+ s.border = 0;
+ s.background = 'none';
+ s.marginTop = '16px';
+
+ button.addEventListener('click', onclick);
+
+ return button;
+};
+
+module.exports = ViewerSelector;
+
+},{"./emitter.js":9,"./util.js":13}],15:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var Util = _dereq_('./util.js');
+
+/**
+ * Android and iOS compatible wakelock implementation.
+ *
+ * Refactored thanks to dkovalev@.
+ */
+function AndroidWakeLock() {
+ var video = document.createElement('video');
+
+ video.addEventListener('ended', function() {
+ video.play();
+ });
+
+ this.request = function() {
+ if (video.paused) {
+ // Base64 version of videos_src/no-sleep-120s.mp4.
+ video.src = Util.base64('video/mp4', 'AAAAGGZ0eXBpc29tAAAAAG1wNDFhdmMxAAAIA21vb3YAAABsbXZoZAAAAADSa9v60mvb+gABX5AAlw/gAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAdkdHJhawAAAFx0a2hkAAAAAdJr2/rSa9v6AAAAAQAAAAAAlw/gAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAQAAAAHAAAAAAAJGVkdHMAAAAcZWxzdAAAAAAAAAABAJcP4AAAAAAAAQAAAAAG3G1kaWEAAAAgbWRoZAAAAADSa9v60mvb+gAPQkAGjneAFccAAAAAAC1oZGxyAAAAAAAAAAB2aWRlAAAAAAAAAAAAAAAAVmlkZW9IYW5kbGVyAAAABodtaW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAZHc3RibAAAAJdzdHNkAAAAAAAAAAEAAACHYXZjMQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAMABwASAAAAEgAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABj//wAAADFhdmNDAWQAC//hABlnZAALrNlfllw4QAAAAwBAAAADAKPFCmWAAQAFaOvssiwAAAAYc3R0cwAAAAAAAAABAAAAbgAPQkAAAAAUc3RzcwAAAAAAAAABAAAAAQAAA4BjdHRzAAAAAAAAAG4AAAABAD0JAAAAAAEAehIAAAAAAQA9CQAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEALcbAAAAAHHN0c2MAAAAAAAAAAQAAAAEAAABuAAAAAQAAAcxzdHN6AAAAAAAAAAAAAABuAAADCQAAABgAAAAOAAAADgAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABMAAAAUc3RjbwAAAAAAAAABAAAIKwAAACt1ZHRhAAAAI6llbmMAFwAAdmxjIDIuMi4xIHN0cmVhbSBvdXRwdXQAAAAId2lkZQAACRRtZGF0AAACrgX//6vcRem95tlIt5Ys2CDZI+7veDI2NCAtIGNvcmUgMTQyIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MzoweDEzIG1lPWhleCBzdWJtZT03IHBzeT0xIHBzeV9yZD0xLjAwOjAuMDAgbWl4ZWRfcmVmPTEgbWVfcmFuZ2U9MTYgY2hyb21hX21lPTEgdHJlbGxpcz0xIDh4OGRjdD0xIGNxbT0wIGRlYWR6b25lPTIxLDExIGZhc3RfcHNraXA9MSBjaHJvbWFfcXBfb2Zmc2V0PS0yIHRocmVhZHM9MTIgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MSBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTEgb3Blbl9nb3A9MCB3ZWlnaHRwPTIga2V5aW50PTI1MCBrZXlpbnRfbWluPTEgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD00MCByYz1hYnIgbWJ0cmVlPTEgYml0cmF0ZT0xMDAgcmF0ZXRvbD0xLjAgcWNvbXA9MC42MCBxcG1pbj0xMCBxcG1heD01MSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAU2WIhAAQ/8ltlOe+cTZuGkKg+aRtuivcDZ0pBsfsEi9p/i1yU9DxS2lq4dXTinViF1URBKXgnzKBd/Uh1bkhHtMrwrRcOJslD01UB+fyaL6ef+DBAAAAFEGaJGxBD5B+v+a+4QqF3MgBXz9MAAAACkGeQniH/+94r6EAAAAKAZ5hdEN/8QytwAAAAAgBnmNqQ3/EgQAAAA5BmmhJqEFomUwIIf/+4QAAAApBnoZFESw//76BAAAACAGepXRDf8SBAAAACAGep2pDf8SAAAAADkGarEmoQWyZTAgh//7gAAAACkGeykUVLD//voEAAAAIAZ7pdEN/xIAAAAAIAZ7rakN/xIAAAAAOQZrwSahBbJlMCCH//uEAAAAKQZ8ORRUsP/++gQAAAAgBny10Q3/EgQAAAAgBny9qQ3/EgAAAAA5BmzRJqEFsmUwIIf/+4AAAAApBn1JFFSw//76BAAAACAGfcXRDf8SAAAAACAGfc2pDf8SAAAAADkGbeEmoQWyZTAgh//7hAAAACkGflkUVLD//voAAAAAIAZ+1dEN/xIEAAAAIAZ+3akN/xIEAAAAOQZu8SahBbJlMCCH//uAAAAAKQZ/aRRUsP/++gQAAAAgBn/l0Q3/EgAAAAAgBn/tqQ3/EgQAAAA5Bm+BJqEFsmUwIIf/+4QAAAApBnh5FFSw//76AAAAACAGePXRDf8SAAAAACAGeP2pDf8SBAAAADkGaJEmoQWyZTAgh//7gAAAACkGeQkUVLD//voEAAAAIAZ5hdEN/xIAAAAAIAZ5jakN/xIEAAAAOQZpoSahBbJlMCCH//uEAAAAKQZ6GRRUsP/++gQAAAAgBnqV0Q3/EgQAAAAgBnqdqQ3/EgAAAAA5BmqxJqEFsmUwIIf/+4AAAAApBnspFFSw//76BAAAACAGe6XRDf8SAAAAACAGe62pDf8SAAAAADkGa8EmoQWyZTAgh//7hAAAACkGfDkUVLD//voEAAAAIAZ8tdEN/xIEAAAAIAZ8vakN/xIAAAAAOQZs0SahBbJlMCCH//uAAAAAKQZ9SRRUsP/++gQAAAAgBn3F0Q3/EgAAAAAgBn3NqQ3/EgAAAAA5Bm3hJqEFsmUwIIf/+4QAAAApBn5ZFFSw//76AAAAACAGftXRDf8SBAAAACAGft2pDf8SBAAAADkGbvEmoQWyZTAgh//7gAAAACkGf2kUVLD//voEAAAAIAZ/5dEN/xIAAAAAIAZ/7akN/xIEAAAAOQZvgSahBbJlMCCH//uEAAAAKQZ4eRRUsP/++gAAAAAgBnj10Q3/EgAAAAAgBnj9qQ3/EgQAAAA5BmiRJqEFsmUwIIf/+4AAAAApBnkJFFSw//76BAAAACAGeYXRDf8SAAAAACAGeY2pDf8SBAAAADkGaaEmoQWyZTAgh//7hAAAACkGehkUVLD//voEAAAAIAZ6ldEN/xIEAAAAIAZ6nakN/xIAAAAAOQZqsSahBbJlMCCH//uAAAAAKQZ7KRRUsP/++gQAAAAgBnul0Q3/EgAAAAAgBnutqQ3/EgAAAAA5BmvBJqEFsmUwIIf/+4QAAAApBnw5FFSw//76BAAAACAGfLXRDf8SBAAAACAGfL2pDf8SAAAAADkGbNEmoQWyZTAgh//7gAAAACkGfUkUVLD//voEAAAAIAZ9xdEN/xIAAAAAIAZ9zakN/xIAAAAAOQZt4SahBbJlMCCH//uEAAAAKQZ+WRRUsP/++gAAAAAgBn7V0Q3/EgQAAAAgBn7dqQ3/EgQAAAA5Bm7xJqEFsmUwIIf/+4AAAAApBn9pFFSw//76BAAAACAGf+XRDf8SAAAAACAGf+2pDf8SBAAAADkGb4EmoQWyZTAgh//7hAAAACkGeHkUVLD//voAAAAAIAZ49dEN/xIAAAAAIAZ4/akN/xIEAAAAOQZokSahBbJlMCCH//uAAAAAKQZ5CRRUsP/++gQAAAAgBnmF0Q3/EgAAAAAgBnmNqQ3/EgQAAAA5BmmhJqEFsmUwIIf/+4QAAAApBnoZFFSw//76BAAAACAGepXRDf8SBAAAACAGep2pDf8SAAAAADkGarEmoQWyZTAgh//7gAAAACkGeykUVLD//voEAAAAIAZ7pdEN/xIAAAAAIAZ7rakN/xIAAAAAPQZruSahBbJlMFEw3//7B');
+ video.play();
+ }
+ };
+
+ this.release = function() {
+ video.pause();
+ video.src = '';
+ };
+}
+
+function iOSWakeLock() {
+ var timer = null;
+
+ this.request = function() {
+ if (!timer) {
+ timer = setInterval(function() {
+ window.location = window.location;
+ setTimeout(window.stop, 0);
+ }, 30000);
+ }
+ }
+
+ this.release = function() {
+ if (timer) {
+ clearInterval(timer);
+ timer = null;
+ }
+ }
+}
+
+
+function getWakeLock() {
+ var userAgent = navigator.userAgent || navigator.vendor || window.opera;
+ if (userAgent.match(/iPhone/i) || userAgent.match(/iPod/i)) {
+ return iOSWakeLock;
+ } else {
+ return AndroidWakeLock;
+ }
+}
+
+module.exports = getWakeLock();
+
+},{"./util.js":13}],16:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var ButtonManager = _dereq_('./button-manager.js');
+var CardboardDistorter = _dereq_('./cardboard-distorter.js');
+var DeviceInfo = _dereq_('./device-info.js');
+var Dpdb = _dereq_('./dpdb.js');
+var Emitter = _dereq_('./emitter.js');
+var Modes = _dereq_('./modes.js');
+var RotateInstructions = _dereq_('./rotate-instructions.js');
+var Util = _dereq_('./util.js');
+var ViewerSelector = _dereq_('./viewer-selector.js');
+var Wakelock = _dereq_('./wakelock.js');
+
+/**
+ * Helper for getting in and out of VR mode.
+ * Here we assume VR mode == full screen mode.
+ *
+ * 1. Detects whether or not VR mode is possible by feature detecting for
+ * WebVR (or polyfill).
+ *
+ * 2. If WebVR is available, shows a button that lets you enter VR mode.
+ *
+ * 3. Provides Cardboard-style distortion if the webvr-polyfill is being used.
+ *
+ * 4. Provides best practices while in VR mode.
+ * - Full screen
+ * - Wake lock
+ * - Orientation lock (mobile only)
+ */
+function WebVRManager(renderer, effect, params) {
+ this.params = params || {};
+
+ this.mode = Modes.UNKNOWN;
+
+ // Set option to hide the button.
+ this.hideButton = this.params.hideButton || false;
+ // Whether or not the FOV should be distorted or un-distorted. By default, it
+ // should be distorted, but in the case of vertex shader based distortion,
+ // ensure that we use undistorted parameters.
+ this.isUndistorted = !!this.params.isUndistorted;
+
+ // Save the THREE.js renderer and effect for later.
+ this.renderer = renderer;
+ this.effect = effect;
+ this.button = new ButtonManager();
+ this.rotateInstructions = new RotateInstructions();
+ this.viewerSelector = new ViewerSelector(DeviceInfo.Viewers);
+
+ // Load the DPDB.
+ var shouldFetch = !WebVRConfig.NO_DPDB_FETCH;
+ this.dpdb = new Dpdb(shouldFetch, this.onDeviceParamsUpdated_.bind(this));
+
+ // Create device info and set the correct default viewer.
+ this.deviceInfo = new DeviceInfo(this.dpdb.getDeviceParams());
+ this.deviceInfo.viewer = DeviceInfo.Viewers[this.viewerSelector.selectedKey];
+ console.log('Using the %s viewer.', this.getViewer().label);
+
+ this.distorter = new CardboardDistorter(renderer);
+ this.distorter.updateDeviceInfo(this.deviceInfo);
+
+ this.isVRCompatible = false;
+ this.isFullscreenDisabled = !!Util.getQueryParameter('no_fullscreen');
+ this.startMode = Modes.NORMAL;
+ var startModeParam = parseInt(Util.getQueryParameter('start_mode'));
+ if (!isNaN(startModeParam)) {
+ this.startMode = startModeParam;
+ }
+
+ // Set the correct viewer profile, but only if this is Cardboard.
+ if (Util.isMobile()) {
+ this.onViewerChanged_(this.getViewer());
+ }
+ // Listen for changes to the viewer.
+ this.viewerSelector.on('change', this.onViewerChanged_.bind(this));
+
+ if (this.hideButton) {
+ this.button.setVisibility(false);
+ }
+
+ // Check if the browser is compatible with WebVR.
+ this.getDeviceByType_(HMDVRDevice).then(function(hmd) {
+ // Activate either VR or Immersive mode.
+ if (WebVRConfig.FORCE_DISTORTION) {
+ this.distorter.setActive(true);
+ this.isVRCompatible = true;
+ } else if (hmd) {
+ this.isVRCompatible = true;
+ // Only enable distortion if we are dealing using the polyfill, we have a
+ // perfect device match, and it's not prevented via configuration.
+ if (hmd.deviceName.indexOf('webvr-polyfill') == 0 && this.deviceInfo.getDevice() &&
+ !WebVRConfig.PREVENT_DISTORTION) {
+ this.distorter.setActive(true);
+ }
+ this.hmd = hmd;
+ }
+ // Set the right mode.
+ switch (this.startMode) {
+ case Modes.MAGIC_WINDOW:
+ this.normalToMagicWindow_();
+ this.setMode_(Modes.MAGIC_WINDOW);
+ break;
+ case Modes.VR:
+ this.anyModeToVR_();
+ this.setMode_(Modes.VR);
+ break;
+ default:
+ this.setMode_(Modes.NORMAL);
+ }
+ this.button.on('fs', this.onFSClick_.bind(this));
+ this.button.on('vr', this.onVRClick_.bind(this));
+ this.button.on('back', this.onBackClick_.bind(this));
+ this.button.on('settings', this.onSettingsClick_.bind(this));
+ this.emit('initialized');
+ }.bind(this));
+
+ // Save the input device for later sending timing data.
+ this.getDeviceByType_(PositionSensorVRDevice).then(function(input) {
+ this.input = input;
+ }.bind(this));
+
+ // Whenever we enter fullscreen, we are entering VR or immersive mode.
+ document.addEventListener('webkitfullscreenchange',
+ this.onFullscreenChange_.bind(this));
+ document.addEventListener('mozfullscreenchange',
+ this.onFullscreenChange_.bind(this));
+ document.addEventListener('MSFullscreenChange',
+ this.onFullscreenChange_.bind(this));
+ window.addEventListener('orientationchange',
+ this.onOrientationChange_.bind(this));
+
+ // Create the necessary elements for wake lock to work.
+ this.wakelock = new Wakelock();
+
+ // Save whether or not we want the touch panner to be enabled or disabled by
+ // default.
+ this.isTouchPannerEnabled = !WebVRConfig.TOUCH_PANNER_DISABLED;
+
+}
+
+WebVRManager.prototype = new Emitter();
+
+// Expose these values externally.
+WebVRManager.Modes = Modes;
+
+/**
+ * Promise returns true if there is at least one HMD device available.
+ */
+WebVRManager.prototype.getDeviceByType_ = function(type) {
+ return new Promise(function(resolve, reject) {
+ navigator.getVRDevices().then(function(devices) {
+ // Promise succeeds, but check if there are any devices actually.
+ for (var i = 0; i < devices.length; i++) {
+ if (devices[i] instanceof type) {
+ resolve(devices[i]);
+ break;
+ }
+ }
+ resolve(null);
+ }, function() {
+ // No devices are found.
+ resolve(null);
+ });
+ });
+};
+
+WebVRManager.prototype.isVRMode = function() {
+ return this.mode == Modes.VR;
+};
+
+WebVRManager.prototype.getViewer = function() {
+ return this.deviceInfo.viewer;
+};
+
+WebVRManager.prototype.getDevice = function() {
+ return this.deviceInfo.device;
+};
+
+WebVRManager.prototype.getDeviceInfo = function() {
+ return this.deviceInfo;
+};
+
+WebVRManager.prototype.render = function(scene, camera, timestamp) {
+ this.camera = camera;
+
+ this.resizeIfNeeded_(camera);
+
+ if (this.isVRMode()) {
+ this.distorter.preRender();
+ this.effect.render(scene, camera);
+ this.distorter.postRender();
+ } else {
+ // Scene may be an array of two scenes, one for each eye.
+ if (scene instanceof Array) {
+ this.renderer.render(scene[0], camera);
+ } else {
+ this.renderer.render(scene, camera);
+ }
+ }
+};
+
+
+WebVRManager.prototype.setMode_ = function(mode) {
+ var oldMode = this.mode;
+ if (mode == this.mode) {
+ console.error('Not changing modes, already in %s', mode);
+ return;
+ }
+ console.log('Mode change: %s => %s', this.mode, mode);
+ this.mode = mode;
+ this.button.setMode(mode, this.isVRCompatible);
+
+ if (this.mode == Modes.VR && Util.isLandscapeMode() && Util.isMobile()) {
+ // In landscape mode, temporarily show the "put into Cardboard"
+ // interstitial. Otherwise, do the default thing.
+ this.rotateInstructions.showTemporarily(3000);
+ } else {
+ this.updateRotateInstructions_();
+ }
+
+ // Also hide the viewer selector.
+ this.viewerSelector.hide();
+
+ // Emit an event indicating the mode changed.
+ this.emit('modechange', mode, oldMode);
+
+ // Note: This is a nasty hack since we need to communicate to the polyfill
+ // that touch panning is disabled, and the only way to do this currently is
+ // via WebVRConfig.
+ // TODO: Maybe move touch panning to the boilerplate to eliminate the hack.
+ //
+ // If we are in VR mode, always disable touch panning.
+ if (this.isTouchPannerEnabled) {
+ if (this.mode == Modes.VR) {
+ WebVRConfig.TOUCH_PANNER_DISABLED = true;
+ } else {
+ WebVRConfig.TOUCH_PANNER_DISABLED = false;
+ }
+ }
+
+ if (this.mode == Modes.VR) {
+ // In VR mode, set the HMDVRDevice parameters.
+ this.setHMDVRDeviceParams_(this.getViewer());
+ }
+};
+
+/**
+ * Main button was clicked.
+ */
+WebVRManager.prototype.onFSClick_ = function() {
+ switch (this.mode) {
+ case Modes.NORMAL:
+ // TODO: Remove this hack when iOS has fullscreen mode.
+ // If this is an iframe on iOS, break out and open in no_fullscreen mode.
+ if (Util.isIOS() && Util.isIFrame()) {
+ var url = window.location.href;
+ url = Util.appendQueryParameter(url, 'no_fullscreen', 'true');
+ url = Util.appendQueryParameter(url, 'start_mode', Modes.MAGIC_WINDOW);
+ top.location.href = url;
+ return;
+ }
+ this.normalToMagicWindow_();
+ this.setMode_(Modes.MAGIC_WINDOW);
+ break;
+ case Modes.MAGIC_WINDOW:
+ if (this.isFullscreenDisabled) {
+ window.history.back();
+ } else {
+ this.anyModeToNormal_();
+ this.setMode_(Modes.NORMAL);
+ }
+ break;
+ }
+};
+
+/**
+ * The VR button was clicked.
+ */
+WebVRManager.prototype.onVRClick_ = function() {
+ // TODO: Remove this hack when iOS has fullscreen mode.
+ // If this is an iframe on iOS, break out and open in no_fullscreen mode.
+ if (this.mode == Modes.NORMAL && Util.isIOS() && Util.isIFrame()) {
+ var url = window.location.href;
+ url = Util.appendQueryParameter(url, 'no_fullscreen', 'true');
+ url = Util.appendQueryParameter(url, 'start_mode', Modes.VR);
+ top.location.href = url;
+ return;
+ }
+ this.anyModeToVR_();
+ this.setMode_(Modes.VR);
+};
+
+/**
+ * Back button was clicked.
+ */
+WebVRManager.prototype.onBackClick_ = function() {
+ if (this.isFullscreenDisabled) {
+ window.history.back();
+ } else {
+ this.anyModeToNormal_();
+ this.setMode_(Modes.NORMAL);
+ }
+};
+
+WebVRManager.prototype.onSettingsClick_ = function() {
+ // Show the viewer selection dialog.
+ this.viewerSelector.show();
+};
+
+/**
+ *
+ * Methods to go between modes.
+ *
+ */
+WebVRManager.prototype.normalToMagicWindow_ = function() {
+ // TODO: Re-enable pointer lock after debugging.
+ //this.requestPointerLock_();
+ this.requestFullscreen_();
+ this.wakelock.request();
+};
+
+WebVRManager.prototype.anyModeToVR_ = function() {
+ // Don't do orientation locking for consistency.
+ //this.requestOrientationLock_();
+ this.requestFullscreen_();
+ //this.effect.setFullScreen(true);
+ this.wakelock.request();
+ this.distorter.patch();
+};
+
+WebVRManager.prototype.vrToMagicWindow_ = function() {
+ //this.releaseOrientationLock_();
+ this.distorter.unpatch();
+
+ // Android bug: when returning from VR, resize the effect.
+ this.resize_();
+}
+
+WebVRManager.prototype.anyModeToNormal_ = function() {
+ //this.effect.setFullScreen(false);
+ this.exitFullscreen_();
+ //this.releaseOrientationLock_();
+ this.releasePointerLock_();
+ this.wakelock.release();
+ this.distorter.unpatch();
+
+ // Android bug: when returning from VR, resize the effect.
+ this.resize_();
+};
+
+WebVRManager.prototype.resizeIfNeeded_ = function(camera) {
+ // Only resize the canvas if it needs to be resized.
+ var size = this.renderer.getSize();
+ if (size.width != window.innerWidth || size.height != window.innerHeight) {
+ this.resize_();
+ }
+};
+
+WebVRManager.prototype.resize_ = function() {
+ this.effect.setSize(window.innerWidth, window.innerHeight);
+ if (this.camera) {
+ this.camera.aspect = window.innerWidth / window.innerHeight;
+ this.camera.updateProjectionMatrix();
+ }
+};
+
+WebVRManager.prototype.onOrientationChange_ = function(e) {
+ this.updateRotateInstructions_();
+ // Also hide the viewer selector.
+ this.viewerSelector.hide();
+};
+
+WebVRManager.prototype.updateRotateInstructions_ = function() {
+ this.rotateInstructions.disableShowTemporarily();
+ // In portrait VR mode, tell the user to rotate to landscape.
+ if (this.mode == Modes.VR && !Util.isLandscapeMode() && Util.isMobile()) {
+ this.rotateInstructions.show();
+ } else {
+ this.rotateInstructions.hide();
+ }
+};
+
+WebVRManager.prototype.onFullscreenChange_ = function(e) {
+ // If we leave full-screen, go back to normal mode.
+ if (document.webkitFullscreenElement === null ||
+ document.mozFullScreenElement === null) {
+ this.anyModeToNormal_();
+ this.setMode_(Modes.NORMAL);
+ }
+};
+
+WebVRManager.prototype.requestPointerLock_ = function() {
+ var canvas = this.renderer.domElement;
+ canvas.requestPointerLock = canvas.requestPointerLock ||
+ canvas.mozRequestPointerLock ||
+ canvas.webkitRequestPointerLock;
+
+ if (canvas.requestPointerLock) {
+ canvas.requestPointerLock();
+ }
+};
+
+WebVRManager.prototype.releasePointerLock_ = function() {
+ document.exitPointerLock = document.exitPointerLock ||
+ document.mozExitPointerLock ||
+ document.webkitExitPointerLock;
+
+ if (document.exitPointerLock) {
+ document.exitPointerLock();
+ }
+};
+
+WebVRManager.prototype.requestOrientationLock_ = function() {
+ if (screen.orientation && Util.isMobile()) {
+ screen.orientation.lock('landscape');
+ }
+};
+
+WebVRManager.prototype.releaseOrientationLock_ = function() {
+ if (screen.orientation) {
+ screen.orientation.unlock();
+ }
+};
+
+WebVRManager.prototype.requestFullscreen_ = function() {
+ var canvas = document.body;
+ //var canvas = this.renderer.domElement;
+ if (canvas.requestFullscreen) {
+ canvas.requestFullscreen();
+ } else if (canvas.mozRequestFullScreen) {
+ canvas.mozRequestFullScreen({vrDisplay: this.hmd});
+ } else if (canvas.webkitRequestFullscreen) {
+ canvas.webkitRequestFullscreen({vrDisplay: this.hmd});
+ } else if (canvas.msRequestFullscreen) {
+ canvas.msRequestFullscreen({vrDisplay: this.hmd});
+ }
+};
+
+WebVRManager.prototype.exitFullscreen_ = function() {
+ if (document.exitFullscreen) {
+ document.exitFullscreen();
+ } else if (document.mozCancelFullScreen) {
+ document.mozCancelFullScreen();
+ } else if (document.webkitExitFullscreen) {
+ document.webkitExitFullscreen();
+ } else if (document.msExitFullscreen) {
+ document.msExitFullscreen();
+ }
+};
+
+WebVRManager.prototype.onViewerChanged_ = function(viewer) {
+ this.deviceInfo.setViewer(viewer);
+
+ // Update the distortion appropriately.
+ this.distorter.updateDeviceInfo(this.deviceInfo);
+
+ // And update the HMDVRDevice parameters.
+ this.setHMDVRDeviceParams_(viewer);
+
+ // Notify anyone interested in this event.
+ this.emit('viewerchange', viewer);
+};
+
+/**
+ * Sets parameters on CardboardHMDVRDevice. These changes are ultimately handled
+ * by VREffect.
+ */
+WebVRManager.prototype.setHMDVRDeviceParams_ = function(viewer) {
+ this.getDeviceByType_(HMDVRDevice).then(function(hmd) {
+ if (!hmd) {
+ return;
+ }
+
+ // If we can set fields of view, do that now.
+ if (hmd.setFieldOfView) {
+ // Calculate the optimal field of view for each eye.
+ hmd.setFieldOfView(this.deviceInfo.getFieldOfViewLeftEye(this.isUndistorted),
+ this.deviceInfo.getFieldOfViewRightEye(this.isUndistorted));
+ }
+
+ // Note: setInterpupillaryDistance is not part of the WebVR standard.
+ if (hmd.setInterpupillaryDistance) {
+ hmd.setInterpupillaryDistance(viewer.interLensDistance);
+ }
+ }.bind(this));
+};
+
+WebVRManager.prototype.onDeviceParamsUpdated_ = function(newParams) {
+ console.log('DPDB reported that device params were updated.');
+ this.deviceInfo.updateDeviceParams(newParams);
+ this.distorter.updateDeviceInfo(this.deviceInfo);
+};
+
+module.exports = WebVRManager;
+
+},{"./button-manager.js":2,"./cardboard-distorter.js":3,"./device-info.js":4,"./dpdb.js":8,"./emitter.js":9,"./modes.js":11,"./rotate-instructions.js":12,"./util.js":13,"./viewer-selector.js":14,"./wakelock.js":15}]},{},[10]);
+
+},{}],7:[function(_dereq_,module,exports){
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof _dereq_=="function"&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof _dereq_=="function"&&_dereq_;for(var o=0;o Util.MAX_TIMESTEP) {
+ console.warn('Invalid timestamps detected. Time step between successive ' +
+ 'gyroscope sensor samples is very small or not monotonic');
+ this.previousTimestampS = timestampS;
+ return;
+ }
+ this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z);
+ this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma);
+
+ // With iOS and Firefox Android, rotationRate is reported in degrees,
+ // so we first convert to radians.
+ if (this.isIOS || this.isFirefoxAndroid) {
+ this.gyroscope.multiplyScalar(Math.PI / 180);
+ }
+
+ this.filter.addAccelMeasurement(this.accelerometer, timestampS);
+ this.filter.addGyroMeasurement(this.gyroscope, timestampS);
+
+ this.previousTimestampS = timestampS;
+};
+
+FusionPositionSensorVRDevice.prototype.onScreenOrientationChange_ =
+ function(screenOrientation) {
+ this.setScreenTransform_();
+};
+
+FusionPositionSensorVRDevice.prototype.setScreenTransform_ = function() {
+ this.worldToScreenQ.set(0, 0, 0, 1);
+ switch (window.orientation) {
+ case 0:
+ break;
+ case 90:
+ this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0, 0, 1), -Math.PI/2);
+ break;
+ case -90:
+ this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI/2);
+ break;
+ case 180:
+ // TODO.
+ break;
+ }
+};
+
+
+module.exports = FusionPositionSensorVRDevice;
+
+},{"./base.js":1,"./complementary-filter.js":3,"./pose-predictor.js":7,"./three-math.js":9,"./touch-panner.js":10,"./util.js":11}],5:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var WebVRPolyfill = _dereq_('./webvr-polyfill.js');
+
+// Initialize a WebVRConfig just in case.
+window.WebVRConfig = window.WebVRConfig || {};
+new WebVRPolyfill();
+
+},{"./webvr-polyfill.js":12}],6:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var PositionSensorVRDevice = _dereq_('./base.js').PositionSensorVRDevice;
+var THREE = _dereq_('./three-math.js');
+var Util = _dereq_('./util.js');
+
+// How much to rotate per key stroke.
+var KEY_SPEED = 0.15;
+var KEY_ANIMATION_DURATION = 80;
+
+// How much to rotate for mouse events.
+var MOUSE_SPEED_X = 0.5;
+var MOUSE_SPEED_Y = 0.3;
+
+/**
+ * A virtual position sensor, implemented using keyboard and
+ * mouse APIs. This is designed as for desktops/laptops where no Device*
+ * events work.
+ */
+function MouseKeyboardPositionSensorVRDevice() {
+ this.deviceId = 'webvr-polyfill:mouse-keyboard';
+ this.deviceName = 'VR Position Device (webvr-polyfill:mouse-keyboard)';
+
+ // Attach to mouse and keyboard events.
+ window.addEventListener('keydown', this.onKeyDown_.bind(this));
+ window.addEventListener('mousemove', this.onMouseMove_.bind(this));
+ window.addEventListener('mousedown', this.onMouseDown_.bind(this));
+ window.addEventListener('mouseup', this.onMouseUp_.bind(this));
+
+ this.phi = 0;
+ this.theta = 0;
+
+ // Variables for keyboard-based rotation animation.
+ this.targetAngle = null;
+
+ // State variables for calculations.
+ this.euler = new THREE.Euler();
+ this.orientation = new THREE.Quaternion();
+
+ // Variables for mouse-based rotation.
+ this.rotateStart = new THREE.Vector2();
+ this.rotateEnd = new THREE.Vector2();
+ this.rotateDelta = new THREE.Vector2();
+}
+MouseKeyboardPositionSensorVRDevice.prototype = new PositionSensorVRDevice();
+
+/**
+ * Returns {orientation: {x,y,z,w}, position: null}.
+ * Position is not supported for parity with other PositionSensors.
+ */
+MouseKeyboardPositionSensorVRDevice.prototype.getState = function() {
+ this.euler.set(this.phi, this.theta, 0, 'YXZ');
+ this.orientation.setFromEuler(this.euler);
+
+ return {
+ hasOrientation: true,
+ orientation: this.orientation,
+ hasPosition: false,
+ position: null
+ }
+};
+
+MouseKeyboardPositionSensorVRDevice.prototype.onKeyDown_ = function(e) {
+ // Track WASD and arrow keys.
+ if (e.keyCode == 38) { // Up key.
+ this.animatePhi_(this.phi + KEY_SPEED);
+ } else if (e.keyCode == 39) { // Right key.
+ this.animateTheta_(this.theta - KEY_SPEED);
+ } else if (e.keyCode == 40) { // Down key.
+ this.animatePhi_(this.phi - KEY_SPEED);
+ } else if (e.keyCode == 37) { // Left key.
+ this.animateTheta_(this.theta + KEY_SPEED);
+ }
+};
+
+MouseKeyboardPositionSensorVRDevice.prototype.animateTheta_ = function(targetAngle) {
+ this.animateKeyTransitions_('theta', targetAngle);
+};
+
+MouseKeyboardPositionSensorVRDevice.prototype.animatePhi_ = function(targetAngle) {
+ // Prevent looking too far up or down.
+ targetAngle = Util.clamp(targetAngle, -Math.PI/2, Math.PI/2);
+ this.animateKeyTransitions_('phi', targetAngle);
+};
+
+/**
+ * Start an animation to transition an angle from one value to another.
+ */
+MouseKeyboardPositionSensorVRDevice.prototype.animateKeyTransitions_ = function(angleName, targetAngle) {
+ // If an animation is currently running, cancel it.
+ if (this.angleAnimation) {
+ clearInterval(this.angleAnimation);
+ }
+ var startAngle = this[angleName];
+ var startTime = new Date();
+ // Set up an interval timer to perform the animation.
+ this.angleAnimation = setInterval(function() {
+ // Once we're finished the animation, we're done.
+ var elapsed = new Date() - startTime;
+ if (elapsed >= KEY_ANIMATION_DURATION) {
+ this[angleName] = targetAngle;
+ clearInterval(this.angleAnimation);
+ return;
+ }
+ // Linearly interpolate the angle some amount.
+ var percent = elapsed / KEY_ANIMATION_DURATION;
+ this[angleName] = startAngle + (targetAngle - startAngle) * percent;
+ }.bind(this), 1000/60);
+};
+
+MouseKeyboardPositionSensorVRDevice.prototype.onMouseDown_ = function(e) {
+ this.rotateStart.set(e.clientX, e.clientY);
+ this.isDragging = true;
+};
+
+// Very similar to https://gist.github.com/mrflix/8351020
+MouseKeyboardPositionSensorVRDevice.prototype.onMouseMove_ = function(e) {
+ if (!this.isDragging && !this.isPointerLocked_()) {
+ return;
+ }
+ // Support pointer lock API.
+ if (this.isPointerLocked_()) {
+ var movementX = e.movementX || e.mozMovementX || 0;
+ var movementY = e.movementY || e.mozMovementY || 0;
+ this.rotateEnd.set(this.rotateStart.x - movementX, this.rotateStart.y - movementY);
+ } else {
+ this.rotateEnd.set(e.clientX, e.clientY);
+ }
+ // Calculate how much we moved in mouse space.
+ this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart);
+ this.rotateStart.copy(this.rotateEnd);
+
+ // Keep track of the cumulative euler angles.
+ var element = document.body;
+ this.phi += 2 * Math.PI * this.rotateDelta.y / element.clientHeight * MOUSE_SPEED_Y;
+ this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * MOUSE_SPEED_X;
+
+ // Prevent looking too far up or down.
+ this.phi = Util.clamp(this.phi, -Math.PI/2, Math.PI/2);
+};
+
+MouseKeyboardPositionSensorVRDevice.prototype.onMouseUp_ = function(e) {
+ this.isDragging = false;
+};
+
+MouseKeyboardPositionSensorVRDevice.prototype.isPointerLocked_ = function() {
+ var el = document.pointerLockElement || document.mozPointerLockElement ||
+ document.webkitPointerLockElement;
+ return el !== undefined;
+};
+
+MouseKeyboardPositionSensorVRDevice.prototype.resetSensor = function() {
+ console.error('Not implemented yet.');
+};
+
+module.exports = MouseKeyboardPositionSensorVRDevice;
+
+},{"./base.js":1,"./three-math.js":9,"./util.js":11}],7:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var THREE = _dereq_('./three-math.js');
+
+var DEBUG = false;
+
+/**
+ * Given an orientation and the gyroscope data, predicts the future orientation
+ * of the head. This makes rendering appear faster.
+ *
+ * Also see: http://msl.cs.uiuc.edu/~lavalle/papers/LavYerKatAnt14.pdf
+ *
+ * @param {Number} predictionTimeS time from head movement to the appearance of
+ * the corresponding image.
+ */
+function PosePredictor(predictionTimeS) {
+ this.predictionTimeS = predictionTimeS;
+
+ // The quaternion corresponding to the previous state.
+ this.previousQ = new THREE.Quaternion();
+ // Previous time a prediction occurred.
+ this.previousTimestampS = null;
+
+ // The delta quaternion that adjusts the current pose.
+ this.deltaQ = new THREE.Quaternion();
+ // The output quaternion.
+ this.outQ = new THREE.Quaternion();
+}
+
+PosePredictor.prototype.getPrediction = function(currentQ, gyro, timestampS) {
+ if (!this.previousTimestampS) {
+ this.previousQ.copy(currentQ);
+ this.previousTimestampS = timestampS;
+ return currentQ;
+ }
+
+ // Calculate axis and angle based on gyroscope rotation rate data.
+ var axis = new THREE.Vector3();
+ axis.copy(gyro);
+ axis.normalize();
+
+ var angularSpeed = gyro.length();
+
+ // If we're rotating slowly, don't do prediction.
+ if (angularSpeed < THREE.Math.degToRad(20)) {
+ if (DEBUG) {
+ console.log('Moving slowly, at %s deg/s: no prediction',
+ THREE.Math.radToDeg(angularSpeed).toFixed(1));
+ }
+ this.outQ.copy(currentQ);
+ this.previousQ.copy(currentQ);
+ return this.outQ;
+ }
+
+ // Get the predicted angle based on the time delta and latency.
+ var deltaT = timestampS - this.previousTimestampS;
+ var predictAngle = angularSpeed * this.predictionTimeS;
+
+ this.deltaQ.setFromAxisAngle(axis, predictAngle);
+ this.outQ.copy(this.previousQ);
+ this.outQ.multiply(this.deltaQ);
+
+ this.previousQ.copy(currentQ);
+
+ return this.outQ;
+};
+
+
+module.exports = PosePredictor;
+
+},{"./three-math.js":9}],8:[function(_dereq_,module,exports){
+function SensorSample(sample, timestampS) {
+ this.set(sample, timestampS);
+};
+
+SensorSample.prototype.set = function(sample, timestampS) {
+ this.sample = sample;
+ this.timestampS = timestampS;
+};
+
+SensorSample.prototype.copy = function(sensorSample) {
+ this.set(sensorSample.sample, sensorSample.timestampS);
+};
+
+module.exports = SensorSample;
+
+},{}],9:[function(_dereq_,module,exports){
+/*
+ * A subset of THREE.js, providing mostly quaternion and euler-related
+ * operations, manually lifted from
+ * https://github.com/mrdoob/three.js/tree/master/src/math, as of 9c30286b38df039fca389989ff06ea1c15d6bad1
+ */
+
+// Only use if the real THREE is not provided.
+var THREE = window.THREE || {};
+
+// If some piece of THREE is missing, fill it in here.
+if (!THREE.Quaternion || !THREE.Vector3 || !THREE.Vector2 || !THREE.Euler || !THREE.Math) {
+console.log('No THREE.js found.');
+
+
+/*** START Quaternion ***/
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://exocortex.com
+ */
+
+THREE.Quaternion = function ( x, y, z, w ) {
+
+ this._x = x || 0;
+ this._y = y || 0;
+ this._z = z || 0;
+ this._w = ( w !== undefined ) ? w : 1;
+
+};
+
+THREE.Quaternion.prototype = {
+
+ constructor: THREE.Quaternion,
+
+ _x: 0,_y: 0, _z: 0, _w: 0,
+
+ get x () {
+
+ return this._x;
+
+ },
+
+ set x ( value ) {
+
+ this._x = value;
+ this.onChangeCallback();
+
+ },
+
+ get y () {
+
+ return this._y;
+
+ },
+
+ set y ( value ) {
+
+ this._y = value;
+ this.onChangeCallback();
+
+ },
+
+ get z () {
+
+ return this._z;
+
+ },
+
+ set z ( value ) {
+
+ this._z = value;
+ this.onChangeCallback();
+
+ },
+
+ get w () {
+
+ return this._w;
+
+ },
+
+ set w ( value ) {
+
+ this._w = value;
+ this.onChangeCallback();
+
+ },
+
+ set: function ( x, y, z, w ) {
+
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._w = w;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ copy: function ( quaternion ) {
+
+ this._x = quaternion.x;
+ this._y = quaternion.y;
+ this._z = quaternion.z;
+ this._w = quaternion.w;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromEuler: function ( euler, update ) {
+
+ if ( euler instanceof THREE.Euler === false ) {
+
+ throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
+ }
+
+ // http://www.mathworks.com/matlabcentral/fileexchange/
+ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
+ // content/SpinCalc.m
+
+ var c1 = Math.cos( euler._x / 2 );
+ var c2 = Math.cos( euler._y / 2 );
+ var c3 = Math.cos( euler._z / 2 );
+ var s1 = Math.sin( euler._x / 2 );
+ var s2 = Math.sin( euler._y / 2 );
+ var s3 = Math.sin( euler._z / 2 );
+
+ if ( euler.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 ( euler.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 ( euler.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 ( euler.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 ( euler.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 ( euler.order === 'XZY' ) {
+
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+
+ }
+
+ if ( update !== false ) this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromAxisAngle: function ( axis, angle ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
+
+ // assumes axis is normalized
+
+ var halfAngle = angle / 2, s = Math.sin( halfAngle );
+
+ this._x = axis.x * s;
+ this._y = axis.y * s;
+ this._z = axis.z * s;
+ this._w = Math.cos( halfAngle );
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromRotationMatrix: function ( m ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ var te = m.elements,
+
+ m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+ m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+ m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
+
+ trace = m11 + m22 + m33,
+ s;
+
+ if ( trace > 0 ) {
+
+ s = 0.5 / Math.sqrt( trace + 1.0 );
+
+ this._w = 0.25 / s;
+ this._x = ( m32 - m23 ) * s;
+ this._y = ( m13 - m31 ) * s;
+ this._z = ( m21 - m12 ) * s;
+
+ } else if ( m11 > m22 && m11 > m33 ) {
+
+ s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
+
+ this._w = ( m32 - m23 ) / s;
+ this._x = 0.25 * s;
+ this._y = ( m12 + m21 ) / s;
+ this._z = ( m13 + m31 ) / s;
+
+ } else if ( m22 > m33 ) {
+
+ s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
+
+ this._w = ( m13 - m31 ) / s;
+ this._x = ( m12 + m21 ) / s;
+ this._y = 0.25 * s;
+ this._z = ( m23 + m32 ) / s;
+
+ } else {
+
+ s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
+
+ this._w = ( m21 - m12 ) / s;
+ this._x = ( m13 + m31 ) / s;
+ this._y = ( m23 + m32 ) / s;
+ this._z = 0.25 * s;
+
+ }
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromUnitVectors: function () {
+
+ // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
+
+ // assumes direction vectors vFrom and vTo are normalized
+
+ var v1, r;
+
+ var EPS = 0.000001;
+
+ return function ( vFrom, vTo ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ r = vFrom.dot( vTo ) + 1;
+
+ if ( r < EPS ) {
+
+ r = 0;
+
+ if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
+
+ v1.set( - vFrom.y, vFrom.x, 0 );
+
+ } else {
+
+ v1.set( 0, - vFrom.z, vFrom.y );
+
+ }
+
+ } else {
+
+ v1.crossVectors( vFrom, vTo );
+
+ }
+
+ this._x = v1.x;
+ this._y = v1.y;
+ this._z = v1.z;
+ this._w = r;
+
+ this.normalize();
+
+ return this;
+
+ }
+
+ }(),
+
+ inverse: function () {
+
+ this.conjugate().normalize();
+
+ return this;
+
+ },
+
+ conjugate: function () {
+
+ this._x *= - 1;
+ this._y *= - 1;
+ this._z *= - 1;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
+
+ },
+
+ lengthSq: function () {
+
+ return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
+
+ },
+
+ normalize: function () {
+
+ var l = this.length();
+
+ if ( l === 0 ) {
+
+ this._x = 0;
+ this._y = 0;
+ this._z = 0;
+ this._w = 1;
+
+ } else {
+
+ l = 1 / l;
+
+ this._x = this._x * l;
+ this._y = this._y * l;
+ this._z = this._z * l;
+ this._w = this._w * l;
+
+ }
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ multiply: function ( q, p ) {
+
+ if ( p !== undefined ) {
+
+ console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
+ return this.multiplyQuaternions( q, p );
+
+ }
+
+ return this.multiplyQuaternions( this, q );
+
+ },
+
+ multiplyQuaternions: function ( a, b ) {
+
+ // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
+
+ var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
+ var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
+
+ this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
+ this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
+ this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
+ this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ multiplyVector3: function ( vector ) {
+
+ console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );
+ return vector.applyQuaternion( this );
+
+ },
+
+ slerp: function ( qb, t ) {
+
+ if ( t === 0 ) return this;
+ if ( t === 1 ) return this.copy( qb );
+
+ var x = this._x, y = this._y, z = this._z, w = this._w;
+
+ // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
+
+ var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
+
+ if ( cosHalfTheta < 0 ) {
+
+ this._w = - qb._w;
+ this._x = - qb._x;
+ this._y = - qb._y;
+ this._z = - qb._z;
+
+ cosHalfTheta = - cosHalfTheta;
+
+ } else {
+
+ this.copy( qb );
+
+ }
+
+ if ( cosHalfTheta >= 1.0 ) {
+
+ this._w = w;
+ this._x = x;
+ this._y = y;
+ this._z = z;
+
+ return this;
+
+ }
+
+ var halfTheta = Math.acos( cosHalfTheta );
+ var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
+
+ if ( Math.abs( sinHalfTheta ) < 0.001 ) {
+
+ this._w = 0.5 * ( w + this._w );
+ this._x = 0.5 * ( x + this._x );
+ this._y = 0.5 * ( y + this._y );
+ this._z = 0.5 * ( z + this._z );
+
+ return this;
+
+ }
+
+ var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
+ ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
+
+ this._w = ( w * ratioA + this._w * ratioB );
+ this._x = ( x * ratioA + this._x * ratioB );
+ this._y = ( y * ratioA + this._y * ratioB );
+ this._z = ( z * ratioA + this._z * ratioB );
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ equals: function ( quaternion ) {
+
+ return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this._x = array[ offset ];
+ this._y = array[ offset + 1 ];
+ this._z = array[ offset + 2 ];
+ this._w = array[ offset + 3 ];
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this._x;
+ array[ offset + 1 ] = this._y;
+ array[ offset + 2 ] = this._z;
+ array[ offset + 3 ] = this._w;
+
+ return array;
+
+ },
+
+ onChange: function ( callback ) {
+
+ this.onChangeCallback = callback;
+
+ return this;
+
+ },
+
+ onChangeCallback: function () {},
+
+ clone: function () {
+
+ return new THREE.Quaternion( this._x, this._y, this._z, this._w );
+
+ }
+
+};
+
+THREE.Quaternion.slerp = function ( qa, qb, qm, t ) {
+
+ return qm.copy( qa ).slerp( qb, t );
+
+}
+
+/*** END Quaternion ***/
+/*** START Vector2 ***/
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author philogb / http://blog.thejit.org/
+ * @author egraether / http://egraether.com/
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ */
+
+THREE.Vector2 = function ( x, y ) {
+
+ this.x = x || 0;
+ this.y = y || 0;
+
+};
+
+THREE.Vector2.prototype = {
+
+ constructor: THREE.Vector2,
+
+ set: function ( x, y ) {
+
+ this.x = x;
+ this.y = y;
+
+ return this;
+
+ },
+
+ setX: function ( x ) {
+
+ this.x = x;
+
+ return this;
+
+ },
+
+ setY: function ( y ) {
+
+ this.y = y;
+
+ return this;
+
+ },
+
+ setComponent: function ( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ getComponent: function ( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+
+ return this;
+
+ },
+
+ multiply: function ( v ) {
+
+ this.x *= v.x;
+ this.y *= v.y;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( s ) {
+
+ this.x *= s;
+ this.y *= s;
+
+ return this;
+
+ },
+
+ divide: function ( v ) {
+
+ this.x /= v.x;
+ this.y /= v.y;
+
+ return this;
+
+ },
+
+ divideScalar: function ( scalar ) {
+
+ if ( scalar !== 0 ) {
+
+ var invScalar = 1 / scalar;
+
+ this.x *= invScalar;
+ this.y *= invScalar;
+
+ } else {
+
+ this.x = 0;
+ this.y = 0;
+
+ }
+
+ return this;
+
+ },
+
+ min: function ( v ) {
+
+ if ( this.x > v.x ) {
+
+ this.x = v.x;
+
+ }
+
+ if ( this.y > v.y ) {
+
+ this.y = v.y;
+
+ }
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ if ( this.x < v.x ) {
+
+ this.x = v.x;
+
+ }
+
+ if ( this.y < v.y ) {
+
+ this.y = v.y;
+
+ }
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // This function assumes min < max, if this assumption isn't true it will not operate correctly
+
+ if ( this.x < min.x ) {
+
+ this.x = min.x;
+
+ } else if ( this.x > max.x ) {
+
+ this.x = max.x;
+
+ }
+
+ if ( this.y < min.y ) {
+
+ this.y = min.y;
+
+ } else if ( this.y > max.y ) {
+
+ this.y = max.y;
+
+ }
+
+ return this;
+ },
+
+ clampScalar: ( function () {
+
+ var min, max;
+
+ return function ( minVal, maxVal ) {
+
+ if ( min === undefined ) {
+
+ min = new THREE.Vector2();
+ max = new THREE.Vector2();
+
+ }
+
+ min.set( minVal, minVal );
+ max.set( maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ } )(),
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y;
+
+ },
+
+ lengthSq: function () {
+
+ return this.x * this.x + this.y * this.y;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this.x * this.x + this.y * this.y );
+
+ },
+
+ normalize: function () {
+
+ return this.divideScalar( this.length() );
+
+ },
+
+ distanceTo: function ( v ) {
+
+ return Math.sqrt( this.distanceToSquared( v ) );
+
+ },
+
+ distanceToSquared: function ( v ) {
+
+ var dx = this.x - v.x, dy = this.y - v.y;
+ return dx * dx + dy * dy;
+
+ },
+
+ setLength: function ( l ) {
+
+ var oldLength = this.length();
+
+ if ( oldLength !== 0 && l !== oldLength ) {
+
+ this.multiplyScalar( l / oldLength );
+ }
+
+ return this;
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+
+ return this;
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+
+ return array;
+
+ },
+
+ fromAttribute: function ( attribute, index, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ index = index * attribute.itemSize + offset;
+
+ this.x = attribute.array[ index ];
+ this.y = attribute.array[ index + 1 ];
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new THREE.Vector2( this.x, this.y );
+
+ }
+
+};
+/*** END Vector2 ***/
+/*** START Vector3 ***/
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author *kile / http://kile.stravaganza.org/
+ * @author philogb / http://blog.thejit.org/
+ * @author mikael emtinger / http://gomo.se/
+ * @author egraether / http://egraether.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.Vector3 = function ( x, y, z ) {
+
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+
+};
+
+THREE.Vector3.prototype = {
+
+ constructor: THREE.Vector3,
+
+ set: function ( x, y, z ) {
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+
+ return this;
+
+ },
+
+ setX: function ( x ) {
+
+ this.x = x;
+
+ return this;
+
+ },
+
+ setY: function ( y ) {
+
+ this.y = y;
+
+ return this;
+
+ },
+
+ setZ: function ( z ) {
+
+ this.z = z;
+
+ return this;
+
+ },
+
+ setComponent: function ( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ case 2: this.z = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ getComponent: function ( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ case 2: return this.z;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+ this.z += v.z;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+ this.z += s;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+ this.z = a.z + b.z;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+ this.z -= v.z;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+ this.z = a.z - b.z;
+
+ return this;
+
+ },
+
+ multiply: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
+ return this.multiplyVectors( v, w );
+
+ }
+
+ this.x *= v.x;
+ this.y *= v.y;
+ this.z *= v.z;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( scalar ) {
+
+ this.x *= scalar;
+ this.y *= scalar;
+ this.z *= scalar;
+
+ return this;
+
+ },
+
+ multiplyVectors: function ( a, b ) {
+
+ this.x = a.x * b.x;
+ this.y = a.y * b.y;
+ this.z = a.z * b.z;
+
+ return this;
+
+ },
+
+ applyEuler: function () {
+
+ var quaternion;
+
+ return function ( euler ) {
+
+ if ( euler instanceof THREE.Euler === false ) {
+
+ console.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
+
+ this.applyQuaternion( quaternion.setFromEuler( euler ) );
+
+ return this;
+
+ };
+
+ }(),
+
+ applyAxisAngle: function () {
+
+ var quaternion;
+
+ return function ( axis, angle ) {
+
+ if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
+
+ this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
+
+ return this;
+
+ };
+
+ }(),
+
+ applyMatrix3: function ( m ) {
+
+ var x = this.x;
+ var y = this.y;
+ var z = this.z;
+
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
+ this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
+ this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
+
+ return this;
+
+ },
+
+ applyMatrix4: function ( m ) {
+
+ // input: THREE.Matrix4 affine matrix
+
+ var x = this.x, y = this.y, z = this.z;
+
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ];
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ];
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ];
+
+ return this;
+
+ },
+
+ applyProjection: function ( m ) {
+
+ // input: THREE.Matrix4 projection matrix
+
+ var x = this.x, y = this.y, z = this.z;
+
+ var e = m.elements;
+ var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide
+
+ this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d;
+ this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d;
+ this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d;
+
+ return this;
+
+ },
+
+ applyQuaternion: function ( q ) {
+
+ var x = this.x;
+ var y = this.y;
+ var z = this.z;
+
+ var qx = q.x;
+ var qy = q.y;
+ var qz = q.z;
+ var qw = q.w;
+
+ // calculate quat * vector
+
+ var ix = qw * x + qy * z - qz * y;
+ var iy = qw * y + qz * x - qx * z;
+ var iz = qw * z + qx * y - qy * x;
+ var iw = - qx * x - qy * y - qz * z;
+
+ // calculate result * inverse quat
+
+ this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
+ this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
+ this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
+
+ return this;
+
+ },
+
+ project: function () {
+
+ var matrix;
+
+ return function ( camera ) {
+
+ if ( matrix === undefined ) matrix = new THREE.Matrix4();
+
+ matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
+ return this.applyProjection( matrix );
+
+ };
+
+ }(),
+
+ unproject: function () {
+
+ var matrix;
+
+ return function ( camera ) {
+
+ if ( matrix === undefined ) matrix = new THREE.Matrix4();
+
+ matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
+ return this.applyProjection( matrix );
+
+ };
+
+ }(),
+
+ transformDirection: function ( m ) {
+
+ // input: THREE.Matrix4 affine matrix
+ // vector interpreted as a direction
+
+ var x = this.x, y = this.y, z = this.z;
+
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
+
+ this.normalize();
+
+ return this;
+
+ },
+
+ divide: function ( v ) {
+
+ this.x /= v.x;
+ this.y /= v.y;
+ this.z /= v.z;
+
+ return this;
+
+ },
+
+ divideScalar: function ( scalar ) {
+
+ if ( scalar !== 0 ) {
+
+ var invScalar = 1 / scalar;
+
+ this.x *= invScalar;
+ this.y *= invScalar;
+ this.z *= invScalar;
+
+ } else {
+
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+
+ }
+
+ return this;
+
+ },
+
+ min: function ( v ) {
+
+ if ( this.x > v.x ) {
+
+ this.x = v.x;
+
+ }
+
+ if ( this.y > v.y ) {
+
+ this.y = v.y;
+
+ }
+
+ if ( this.z > v.z ) {
+
+ this.z = v.z;
+
+ }
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ if ( this.x < v.x ) {
+
+ this.x = v.x;
+
+ }
+
+ if ( this.y < v.y ) {
+
+ this.y = v.y;
+
+ }
+
+ if ( this.z < v.z ) {
+
+ this.z = v.z;
+
+ }
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // This function assumes min < max, if this assumption isn't true it will not operate correctly
+
+ if ( this.x < min.x ) {
+
+ this.x = min.x;
+
+ } else if ( this.x > max.x ) {
+
+ this.x = max.x;
+
+ }
+
+ if ( this.y < min.y ) {
+
+ this.y = min.y;
+
+ } else if ( this.y > max.y ) {
+
+ this.y = max.y;
+
+ }
+
+ if ( this.z < min.z ) {
+
+ this.z = min.z;
+
+ } else if ( this.z > max.z ) {
+
+ this.z = max.z;
+
+ }
+
+ return this;
+
+ },
+
+ clampScalar: ( function () {
+
+ var min, max;
+
+ return function ( minVal, maxVal ) {
+
+ if ( min === undefined ) {
+
+ min = new THREE.Vector3();
+ max = new THREE.Vector3();
+
+ }
+
+ min.set( minVal, minVal, minVal );
+ max.set( maxVal, maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ } )(),
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+ this.z = Math.floor( this.z );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+ this.z = Math.ceil( this.z );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+ this.z = Math.round( this.z );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+ this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+ this.z = - this.z;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y + this.z * v.z;
+
+ },
+
+ 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 );
+
+ },
+
+ lengthManhattan: function () {
+
+ return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
+
+ },
+
+ normalize: function () {
+
+ return this.divideScalar( this.length() );
+
+ },
+
+ setLength: function ( l ) {
+
+ var oldLength = this.length();
+
+ if ( oldLength !== 0 && l !== oldLength ) {
+
+ this.multiplyScalar( l / oldLength );
+ }
+
+ return this;
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+ this.z += ( v.z - this.z ) * alpha;
+
+ return this;
+
+ },
+
+ cross: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
+ return this.crossVectors( v, w );
+
+ }
+
+ var x = this.x, y = this.y, z = this.z;
+
+ this.x = y * v.z - z * v.y;
+ this.y = z * v.x - x * v.z;
+ this.z = x * v.y - y * v.x;
+
+ return this;
+
+ },
+
+ crossVectors: function ( a, b ) {
+
+ var ax = a.x, ay = a.y, az = a.z;
+ var bx = b.x, by = b.y, bz = b.z;
+
+ this.x = ay * bz - az * by;
+ this.y = az * bx - ax * bz;
+ this.z = ax * by - ay * bx;
+
+ return this;
+
+ },
+
+ projectOnVector: function () {
+
+ var v1, dot;
+
+ return function ( vector ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ v1.copy( vector ).normalize();
+
+ dot = this.dot( v1 );
+
+ return this.copy( v1 ).multiplyScalar( dot );
+
+ };
+
+ }(),
+
+ projectOnPlane: function () {
+
+ var v1;
+
+ return function ( planeNormal ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ v1.copy( this ).projectOnVector( planeNormal );
+
+ return this.sub( v1 );
+
+ }
+
+ }(),
+
+ reflect: function () {
+
+ // reflect incident vector off plane orthogonal to normal
+ // normal is assumed to have unit length
+
+ var v1;
+
+ return function ( normal ) {
+
+ if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+ return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
+
+ }
+
+ }(),
+
+ angleTo: function ( v ) {
+
+ var theta = this.dot( v ) / ( this.length() * v.length() );
+
+ // clamp, to handle numerical problems
+
+ return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) );
+
+ },
+
+ distanceTo: function ( v ) {
+
+ return Math.sqrt( this.distanceToSquared( v ) );
+
+ },
+
+ distanceToSquared: function ( v ) {
+
+ var dx = this.x - v.x;
+ var dy = this.y - v.y;
+ var dz = this.z - v.z;
+
+ return dx * dx + dy * dy + dz * dz;
+
+ },
+
+ setEulerFromRotationMatrix: function ( m, order ) {
+
+ console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' );
+
+ },
+
+ setEulerFromQuaternion: function ( q, order ) {
+
+ console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' );
+
+ },
+
+ getPositionFromMatrix: function ( m ) {
+
+ console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' );
+
+ return this.setFromMatrixPosition( m );
+
+ },
+
+ getScaleFromMatrix: function ( m ) {
+
+ console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' );
+
+ return this.setFromMatrixScale( m );
+ },
+
+ getColumnFromMatrix: function ( index, matrix ) {
+
+ console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' );
+
+ return this.setFromMatrixColumn( index, matrix );
+
+ },
+
+ setFromMatrixPosition: function ( m ) {
+
+ this.x = m.elements[ 12 ];
+ this.y = m.elements[ 13 ];
+ this.z = m.elements[ 14 ];
+
+ return this;
+
+ },
+
+ setFromMatrixScale: function ( m ) {
+
+ var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length();
+ var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length();
+ var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length();
+
+ this.x = sx;
+ this.y = sy;
+ this.z = sz;
+
+ return this;
+ },
+
+ setFromMatrixColumn: function ( index, matrix ) {
+
+ var offset = index * 4;
+
+ var me = matrix.elements;
+
+ this.x = me[ offset ];
+ this.y = me[ offset + 1 ];
+ this.z = me[ offset + 2 ];
+
+ return this;
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+ this.z = array[ offset + 2 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+ array[ offset + 2 ] = this.z;
+
+ return array;
+
+ },
+
+ fromAttribute: function ( attribute, index, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ index = index * attribute.itemSize + offset;
+
+ this.x = attribute.array[ index ];
+ this.y = attribute.array[ index + 1 ];
+ this.z = attribute.array[ index + 2 ];
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new THREE.Vector3( this.x, this.y, this.z );
+
+ }
+
+};
+/*** END Vector3 ***/
+/*** START Euler ***/
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://exocortex.com
+ */
+
+THREE.Euler = function ( x, y, z, order ) {
+
+ this._x = x || 0;
+ this._y = y || 0;
+ this._z = z || 0;
+ this._order = order || THREE.Euler.DefaultOrder;
+
+};
+
+THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];
+
+THREE.Euler.DefaultOrder = 'XYZ';
+
+THREE.Euler.prototype = {
+
+ constructor: THREE.Euler,
+
+ _x: 0, _y: 0, _z: 0, _order: THREE.Euler.DefaultOrder,
+
+ get x () {
+
+ return this._x;
+
+ },
+
+ set x ( value ) {
+
+ this._x = value;
+ this.onChangeCallback();
+
+ },
+
+ get y () {
+
+ return this._y;
+
+ },
+
+ set y ( value ) {
+
+ this._y = value;
+ this.onChangeCallback();
+
+ },
+
+ get z () {
+
+ return this._z;
+
+ },
+
+ set z ( value ) {
+
+ this._z = value;
+ this.onChangeCallback();
+
+ },
+
+ get order () {
+
+ return this._order;
+
+ },
+
+ set order ( value ) {
+
+ this._order = value;
+ this.onChangeCallback();
+
+ },
+
+ set: function ( x, y, z, order ) {
+
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._order = order || this._order;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ copy: function ( euler ) {
+
+ this._x = euler._x;
+ this._y = euler._y;
+ this._z = euler._z;
+ this._order = euler._order;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromRotationMatrix: function ( m, order, update ) {
+
+ var clamp = THREE.Math.clamp;
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ var te = m.elements;
+ var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
+ var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
+ var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
+
+ order = order || this._order;
+
+ if ( order === 'XYZ' ) {
+
+ this._y = Math.asin( clamp( m13, - 1, 1 ) );
+
+ if ( Math.abs( m13 ) < 0.99999 ) {
+
+ this._x = Math.atan2( - m23, m33 );
+ this._z = Math.atan2( - m12, m11 );
+
+ } else {
+
+ this._x = Math.atan2( m32, m22 );
+ this._z = 0;
+
+ }
+
+ } else if ( order === 'YXZ' ) {
+
+ this._x = Math.asin( - clamp( m23, - 1, 1 ) );
+
+ if ( Math.abs( m23 ) < 0.99999 ) {
+
+ this._y = Math.atan2( m13, m33 );
+ this._z = Math.atan2( m21, m22 );
+
+ } else {
+
+ this._y = Math.atan2( - m31, m11 );
+ this._z = 0;
+
+ }
+
+ } else if ( order === 'ZXY' ) {
+
+ this._x = Math.asin( clamp( m32, - 1, 1 ) );
+
+ if ( Math.abs( m32 ) < 0.99999 ) {
+
+ this._y = Math.atan2( - m31, m33 );
+ this._z = Math.atan2( - m12, m22 );
+
+ } else {
+
+ this._y = 0;
+ this._z = Math.atan2( m21, m11 );
+
+ }
+
+ } else if ( order === 'ZYX' ) {
+
+ this._y = Math.asin( - clamp( m31, - 1, 1 ) );
+
+ if ( Math.abs( m31 ) < 0.99999 ) {
+
+ this._x = Math.atan2( m32, m33 );
+ this._z = Math.atan2( m21, m11 );
+
+ } else {
+
+ this._x = 0;
+ this._z = Math.atan2( - m12, m22 );
+
+ }
+
+ } else if ( order === 'YZX' ) {
+
+ this._z = Math.asin( clamp( m21, - 1, 1 ) );
+
+ if ( Math.abs( m21 ) < 0.99999 ) {
+
+ this._x = Math.atan2( - m23, m22 );
+ this._y = Math.atan2( - m31, m11 );
+
+ } else {
+
+ this._x = 0;
+ this._y = Math.atan2( m13, m33 );
+
+ }
+
+ } else if ( order === 'XZY' ) {
+
+ this._z = Math.asin( - clamp( m12, - 1, 1 ) );
+
+ if ( Math.abs( m12 ) < 0.99999 ) {
+
+ this._x = Math.atan2( m32, m22 );
+ this._y = Math.atan2( m13, m11 );
+
+ } else {
+
+ this._x = Math.atan2( - m23, m33 );
+ this._y = 0;
+
+ }
+
+ } else {
+
+ console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order )
+
+ }
+
+ this._order = order;
+
+ if ( update !== false ) this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromQuaternion: function () {
+
+ var matrix;
+
+ return function ( q, order, update ) {
+
+ if ( matrix === undefined ) matrix = new THREE.Matrix4();
+ matrix.makeRotationFromQuaternion( q );
+ this.setFromRotationMatrix( matrix, order, update );
+
+ return this;
+
+ };
+
+ }(),
+
+ setFromVector3: function ( v, order ) {
+
+ return this.set( v.x, v.y, v.z, order || this._order );
+
+ },
+
+ reorder: function () {
+
+ // WARNING: this discards revolution information -bhouston
+
+ var q = new THREE.Quaternion();
+
+ return function ( newOrder ) {
+
+ q.setFromEuler( this );
+ this.setFromQuaternion( q, newOrder );
+
+ };
+
+ }(),
+
+ equals: function ( euler ) {
+
+ return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
+
+ },
+
+ fromArray: function ( array ) {
+
+ this._x = array[ 0 ];
+ this._y = array[ 1 ];
+ this._z = array[ 2 ];
+ if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ toArray: function () {
+
+ return [ this._x, this._y, this._z, this._order ];
+
+ },
+
+ toVector3: function ( optionalResult ) {
+
+ if ( optionalResult ) {
+
+ return optionalResult.set( this._x, this._y, this._z );
+
+ } else {
+
+ return new THREE.Vector3( this._x, this._y, this._z );
+
+ }
+
+ },
+
+ onChange: function ( callback ) {
+
+ this.onChangeCallback = callback;
+
+ return this;
+
+ },
+
+ onChangeCallback: function () {},
+
+ clone: function () {
+
+ return new THREE.Euler( this._x, this._y, this._z, this._order );
+
+ }
+
+};
+/*** END Euler ***/
+/*** START Math ***/
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Math = {
+
+ generateUUID: function () {
+
+ // http://www.broofa.com/Tools/Math.uuid.htm
+
+ var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' );
+ var uuid = new Array( 36 );
+ var rnd = 0, r;
+
+ return function () {
+
+ for ( var i = 0; i < 36; i ++ ) {
+
+ if ( i == 8 || i == 13 || i == 18 || i == 23 ) {
+
+ uuid[ i ] = '-';
+
+ } else if ( i == 14 ) {
+
+ uuid[ i ] = '4';
+
+ } else {
+
+ if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
+ r = rnd & 0xf;
+ rnd = rnd >> 4;
+ uuid[ i ] = chars[ ( i == 19 ) ? ( r & 0x3 ) | 0x8 : r ];
+
+ }
+ }
+
+ return uuid.join( '' );
+
+ };
+
+ }(),
+
+ // Clamp value to range
+
+ clamp: function ( x, a, b ) {
+
+ return ( x < a ) ? a : ( ( x > b ) ? b : x );
+
+ },
+
+ // Clamp value to range to range
+
+ mapLinear: function ( x, a1, a2, b1, b2 ) {
+
+ return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
+
+ },
+
+ // http://en.wikipedia.org/wiki/Smoothstep
+
+ smoothstep: function ( x, min, max ) {
+
+ if ( x <= min ) return 0;
+ if ( x >= max ) return 1;
+
+ x = ( x - min ) / ( max - min );
+
+ return x * x * ( 3 - 2 * x );
+
+ },
+
+ smootherstep: function ( x, min, max ) {
+
+ if ( x <= min ) return 0;
+ if ( x >= max ) return 1;
+
+ x = ( x - min ) / ( max - min );
+
+ return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
+
+ },
+
+ // Random float from <0, 1> with 16 bits of randomness
+ // (standard Math.random() creates repetitive patterns when applied over larger space)
+
+ random16: function () {
+
+ return ( 65280 * Math.random() + 255 * Math.random() ) / 65535;
+
+ },
+
+ // Random integer from interval
+
+ randInt: function ( low, high ) {
+
+ return Math.floor( this.randFloat( low, high ) );
+
+ },
+
+ // Random float from interval
+
+ randFloat: function ( low, high ) {
+
+ return low + Math.random() * ( high - low );
+
+ },
+
+ // Random float from <-range/2, range/2> interval
+
+ randFloatSpread: function ( range ) {
+
+ return range * ( 0.5 - Math.random() );
+
+ },
+
+ degToRad: function () {
+
+ var degreeToRadiansFactor = Math.PI / 180;
+
+ return function ( degrees ) {
+
+ return degrees * degreeToRadiansFactor;
+
+ };
+
+ }(),
+
+ radToDeg: function () {
+
+ var radianToDegreesFactor = 180 / Math.PI;
+
+ return function ( radians ) {
+
+ return radians * radianToDegreesFactor;
+
+ };
+
+ }(),
+
+ isPowerOfTwo: function ( value ) {
+
+ return ( value & ( value - 1 ) ) === 0 && value !== 0;
+
+ },
+
+ nextPowerOfTwo: function ( value ) {
+
+ value --;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ value ++;
+
+ return value;
+ }
+
+};
+
+/*** END Math ***/
+
+}
+
+module.exports = THREE;
+
+},{}],10:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var THREE = _dereq_('./three-math.js');
+var Util = _dereq_('./util.js');
+
+var ROTATE_SPEED = 0.5;
+/**
+ * Provides a quaternion responsible for pre-panning the scene before further
+ * transformations due to device sensors.
+ */
+function TouchPanner() {
+ window.addEventListener('touchstart', this.onTouchStart_.bind(this));
+ window.addEventListener('touchmove', this.onTouchMove_.bind(this));
+ window.addEventListener('touchend', this.onTouchEnd_.bind(this));
+
+ this.isTouching = false;
+ this.rotateStart = new THREE.Vector2();
+ this.rotateEnd = new THREE.Vector2();
+ this.rotateDelta = new THREE.Vector2();
+
+ this.theta = 0;
+ this.orientation = new THREE.Quaternion();
+}
+
+TouchPanner.prototype.getOrientation = function() {
+ this.orientation.setFromEuler(new THREE.Euler(0, 0, this.theta));
+ return this.orientation;
+};
+
+TouchPanner.prototype.resetSensor = function() {
+ this.theta = 0;
+};
+
+TouchPanner.prototype.onTouchStart_ = function(e) {
+ // Only respond if there is exactly one touch.
+ if (e.touches.length != 1) {
+ return;
+ }
+ this.rotateStart.set(e.touches[0].pageX, e.touches[0].pageY);
+ this.isTouching = true;
+};
+
+TouchPanner.prototype.onTouchMove_ = function(e) {
+ if (!this.isTouching) {
+ return;
+ }
+ this.rotateEnd.set(e.touches[0].pageX, e.touches[0].pageY);
+ this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart);
+ this.rotateStart.copy(this.rotateEnd);
+
+ // On iOS, direction is inverted.
+ if (Util.isIOS()) {
+ this.rotateDelta.x *= -1;
+ }
+
+ var element = document.body;
+ this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * ROTATE_SPEED;
+};
+
+TouchPanner.prototype.onTouchEnd_ = function(e) {
+ this.isTouching = false;
+};
+
+module.exports = TouchPanner;
+
+},{"./three-math.js":9,"./util.js":11}],11:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var Util = window.Util || {};
+
+Util.MIN_TIMESTEP = 0.001;
+Util.MAX_TIMESTEP = 1;
+
+Util.clamp = function(value, min, max) {
+ return Math.min(Math.max(min, value), max);
+};
+
+Util.isIOS = function() {
+ return /iPad|iPhone|iPod/.test(navigator.platform);
+};
+
+Util.isFirefoxAndroid = function() {
+ return navigator.userAgent.indexOf('Firefox') !== -1 && navigator.userAgent.indexOf('Android') !== -1;
+}
+
+// Helper method to validate the time steps of sensor timestamps.
+Util.isTimestampDeltaValid = function(timestampDeltaS) {
+ if (isNaN(timestampDeltaS)) {
+ return false;
+ }
+ if (timestampDeltaS <= Util.MIN_TIMESTEP) {
+ return false;
+ }
+ if (timestampDeltaS > Util.MAX_TIMESTEP) {
+ return false;
+ }
+ return true;
+}
+
+module.exports = Util;
+
+},{}],12:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var CardboardHMDVRDevice = _dereq_('./cardboard-hmd-vr-device.js');
+//var OrientationPositionSensorVRDevice = require('./orientation-position-sensor-vr-device.js');
+var FusionPositionSensorVRDevice = _dereq_('./fusion-position-sensor-vr-device.js');
+var MouseKeyboardPositionSensorVRDevice = _dereq_('./mouse-keyboard-position-sensor-vr-device.js');
+// Uncomment to add positional tracking via webcam.
+//var WebcamPositionSensorVRDevice = require('./webcam-position-sensor-vr-device.js');
+var HMDVRDevice = _dereq_('./base.js').HMDVRDevice;
+var PositionSensorVRDevice = _dereq_('./base.js').PositionSensorVRDevice;
+
+function WebVRPolyfill() {
+ this.devices = [];
+
+ if (!this.isWebVRAvailable()) {
+ this.enablePolyfill();
+ }
+}
+
+WebVRPolyfill.prototype.isWebVRAvailable = function() {
+ return ('getVRDevices' in navigator) || ('mozGetVRDevices' in navigator);
+};
+
+
+WebVRPolyfill.prototype.enablePolyfill = function() {
+ // Initialize our virtual VR devices.
+ if (this.isCardboardCompatible()) {
+ this.devices.push(new CardboardHMDVRDevice());
+ }
+
+ // Polyfill using the right position sensor.
+ if (this.isMobile()) {
+ //this.devices.push(new OrientationPositionSensorVRDevice());
+ this.devices.push(new FusionPositionSensorVRDevice());
+ } else {
+ if (!WebVRConfig.MOUSE_KEYBOARD_CONTROLS_DISABLED) {
+ this.devices.push(new MouseKeyboardPositionSensorVRDevice());
+ }
+ // Uncomment to add positional tracking via webcam.
+ //this.devices.push(new WebcamPositionSensorVRDevice());
+ }
+
+ // Provide navigator.getVRDevices.
+ navigator.getVRDevices = this.getVRDevices.bind(this);
+
+ // Provide the CardboardHMDVRDevice and PositionSensorVRDevice objects.
+ window.HMDVRDevice = HMDVRDevice;
+ window.PositionSensorVRDevice = PositionSensorVRDevice;
+};
+
+WebVRPolyfill.prototype.getVRDevices = function() {
+ var devices = this.devices;
+ return new Promise(function(resolve, reject) {
+ try {
+ resolve(devices);
+ } catch (e) {
+ reject(e);
+ }
+ });
+};
+
+/**
+ * Determine if a device is mobile.
+ */
+WebVRPolyfill.prototype.isMobile = function() {
+ return /Android/i.test(navigator.userAgent) ||
+ /iPhone|iPad|iPod/i.test(navigator.userAgent);
+};
+
+WebVRPolyfill.prototype.isCardboardCompatible = function() {
+ // For now, support all iOS and Android devices.
+ // Also enable the WebVRConfig.FORCE_VR flag for debugging.
+ return this.isMobile() || WebVRConfig.FORCE_ENABLE_VR;
+};
+
+module.exports = WebVRPolyfill;
+
+},{"./base.js":1,"./cardboard-hmd-vr-device.js":2,"./fusion-position-sensor-vr-device.js":4,"./mouse-keyboard-position-sensor-vr-device.js":6}]},{},[5]);
+
+},{}],8:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Sits in an embedded iframe, receiving DeviceMotion messages from a containing
+ * iFrame. These messages are converted into synthetic DeviceMotion events.
+ *
+ * This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=150072.
+ */
+function DeviceMotionReceiver() {
+ window.addEventListener('message', this.onMessage_.bind(this), false);
+}
+
+DeviceMotionReceiver.prototype.onMessage_ = function(event) {
+ var message = event.data;
+ if (message.type !== 'DeviceMotion') {
+ console.warn('Got unknown message of type %s from %s', message.type, message.origin);
+ return;
+ }
+
+ console.log('onMessage_', event);
+
+ // Synthesize a DeviceMotion event.
+ this.synthesizeDeviceMotionEvent_(message.deviceMotionEvent);
+};
+
+DeviceMotionReceiver.prototype.synthesizeDeviceMotionEvent_ = function(eventData) {
+ var type = 'devicemotion-iframe';
+ var canBubble = false;
+ var cancelable = false;
+
+ var dme = document.createEvent('DeviceMotionEvent');
+ dme.initDeviceMotionEvent(type, canBubble, cancelable,
+ eventData.acceleration,
+ eventData.accelerationIncludingGravity,
+ eventData.rotationRate,
+ eventData.interval);
+
+ window.dispatchEvent(dme);
+};
+
+module.exports = DeviceMotionReceiver;
+
+},{}],9:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+function Emitter() {
+ this.initEmitter();
+}
+
+Emitter.prototype.initEmitter = function() {
+ this.callbacks = {};
+};
+
+Emitter.prototype.emit = function(eventName) {
+ var callbacks = this.callbacks[eventName];
+ if (!callbacks) {
+ console.log('No valid callback specified.');
+ return;
+ }
+ var args = [].slice.call(arguments)
+ // Eliminate the first param (the callback).
+ args.shift();
+ for (var i = 0; i < callbacks.length; i++) {
+ callbacks[i].apply(this, args);
+ }
+};
+
+Emitter.prototype.on = function(eventName, callback) {
+ if (eventName in this.callbacks) {
+ var cbs = this.callbacks[eventName]
+ if (cbs.indexOf(callback) == -1) {
+ cbs.push(callback);
+ }
+ } else {
+ this.callbacks[eventName] = [callback];
+ }
+};
+
+Emitter.prototype.removeListener = function(eventName, callback) {
+ if (!(eventName in this.callbacks)) {
+ return;
+ }
+ var cbs = this.callbacks[eventName];
+ var ind = cbs.indexOf(callback);
+ if (ind == -1) {
+ console.warn('No matching callback found');
+ return;
+ }
+ cbs.splice(ind, 1);
+};
+
+module.exports = Emitter;
+
+},{}],10:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var Eyes = {
+ LEFT: 0,
+ RIGHT: 1
+};
+
+module.exports = Eyes;
+
+},{}],11:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Shows a 2D loading indicator while various pieces of EmbedVR load.
+ */
+function LoadingIndicator() {
+ this.el = this.build_();
+ document.body.appendChild(this.el);
+ this.show();
+}
+
+LoadingIndicator.prototype.build_ = function() {
+ var overlay = document.createElement('div');
+ var s = overlay.style;
+ s.position = 'fixed';
+ s.top = 0;
+ s.left = 0;
+ s.width = '100%';
+ s.height = '100%';
+ s.background = '#eee';
+ var img = document.createElement('img');
+ img.src = 'images/loading.gif';
+ var s = img.style;
+ s.position = 'absolute';
+ s.top = '50%';
+ s.left = '50%';
+ s.transform = 'translate(-50%, -50%)';
+
+ overlay.appendChild(img);
+ return overlay;
+};
+
+LoadingIndicator.prototype.hide = function() {
+ this.el.style.display = 'none';
+};
+
+LoadingIndicator.prototype.show = function() {
+ this.el.style.display = 'block';
+};
+
+module.exports = LoadingIndicator;
+
+},{}],12:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Disable distortion provided by the boilerplate because we are doing
+// vertex-based distortion.
+WebVRConfig = window.WebVRConfig || {}
+WebVRConfig.PREVENT_DISTORTION = true;
+
+// Initialize the loading indicator as quickly as possible to give the user
+// immediate feedback.
+var LoadingIndicator = _dereq_('./loading-indicator');
+var loadIndicator = new LoadingIndicator();
+
+// Include relevant polyfills.
+_dereq_('../node_modules/webvr-polyfill/build/webvr-polyfill');
+var ES6Promise = _dereq_('../node_modules/es6-promise/dist/es6-promise.min');
+// Polyfill ES6 promises for IE.
+ES6Promise.polyfill();
+
+var PhotosphereRenderer = _dereq_('./photosphere-renderer');
+var SceneLoader = _dereq_('./scene-loader');
+var Stats = _dereq_('../node_modules/stats-js/build/stats.min');
+var Util = _dereq_('./util');
+
+// Include the DeviceMotionReceiver for the iOS cross domain iframe workaround.
+// This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=150072.
+var DeviceMotionReceiver = _dereq_('./device-motion-receiver');
+var dmr = new DeviceMotionReceiver();
+
+
+window.addEventListener('load', init);
+
+var stats = new Stats();
+
+var loader = new SceneLoader();
+loader.on('error', onSceneError);
+loader.on('load', onSceneLoad);
+
+var renderer = new PhotosphereRenderer();
+renderer.on('error', onRenderError);
+
+var videoElement = null;
+// TODO: Make this not global.
+// Currently global in order to allow callbacks.
+var loadedScene = null;
+
+function init() {
+ if (!Util.isWebGLEnabled()) {
+ showError('WebGL not supported.');
+ return;
+ }
+ // Load the scene.
+ loader.loadScene();
+
+ if (Util.getQueryParameter('debug')) {
+ showStats();
+ }
+}
+
+function loadImage(src, params) {
+ renderer.on('load', onRenderLoad);
+ renderer.setPhotosphere(src, params);
+}
+
+function onSceneLoad(scene) {
+ if (!scene || !scene.isComplete()) {
+ showError('Scene failed to load');
+ return;
+ }
+
+ loadedScene = scene;
+
+ var params = {
+ isStereo: scene.isStereo,
+ }
+ renderer.setDefaultLookDirection(scene.yaw || 0);
+
+ if (scene.preview) {
+ var onPreviewLoad = function() {
+ loadIndicator.hide();
+ renderer.removeListener('load', onPreviewLoad);
+ renderer.setPhotosphere(scene.image, params);
+ }
+ renderer.removeListener('load', onRenderLoad);
+ renderer.on('load', onPreviewLoad);
+ renderer.setPhotosphere(scene.preview, params);
+ } else if (scene.video) {
+ if (Util.isIOS() || Util.isIE11()) {
+ // On iOS and IE 11, if an 'image' param is provided, load it instead of
+ // showing an error.
+ //
+ // TODO(smus): Once video textures are supported, remove this fallback.
+ if (scene.image) {
+ loadImage(scene.image, params);
+ } else {
+ showError('Video is not supported on this platform (iOS or IE11).');
+ }
+ } else {
+ // Load the video element.
+ videoElement = document.createElement('video');
+ videoElement.src = scene.video;
+ videoElement.loop = true;
+ videoElement.setAttribute('crossorigin', 'anonymous');
+ videoElement.addEventListener('canplaythrough', onVideoLoad);
+ videoElement.addEventListener('error', onVideoError);
+ }
+ } else if (scene.image) {
+ // Otherwise, just render the photosphere.
+ loadImage(scene.image, params);
+ }
+
+ console.log('Loaded scene', scene);
+}
+
+function onVideoLoad() {
+ // Render the stereo video.
+ var params = {
+ isStereo: loadedScene.isStereo,
+ }
+ renderer.set360Video(videoElement, params);
+
+ // On mobile, tell the user they need to tap to start. Otherwise, autoplay.
+ if (!Util.isMobile()) {
+ // Hide loading indicator.
+ loadIndicator.hide();
+ // Autoplay the video on desktop.
+ videoElement.play();
+ } else {
+ // Tell user to tap to start.
+ showError('Tap to start video', 'Play');
+ document.body.addEventListener('touchend', onVideoTap);
+ }
+
+ // Prevent onVideoLoad from firing multiple times.
+ videoElement.removeEventListener('canplaythrough', onVideoLoad);
+}
+
+function onVideoTap() {
+ hideError();
+ videoElement.play();
+
+ // Prevent multiple play() calls on the video element.
+ document.body.removeEventListener('touchend', onVideoTap);
+}
+
+function onRenderLoad() {
+ // Hide loading indicator.
+ loadIndicator.hide();
+}
+
+function onSceneError(message) {
+ showError('Loader: ' + message);
+}
+
+function onRenderError(message) {
+ showError('Render: ' + message);
+}
+
+function onVideoError(e) {
+ showError('Video load error');
+ console.log(e);
+}
+
+function showError(message, opt_title) {
+ // Hide loading indicator.
+ loadIndicator.hide();
+
+ var error = document.querySelector('#error');
+ error.classList.add('visible');
+ error.querySelector('.message').innerHTML = message;
+
+ var title = (opt_title !== undefined ? opt_title : 'Error');
+ error.querySelector('.title').innerHTML = title;
+}
+
+function hideError() {
+ var error = document.querySelector('#error');
+ error.classList.remove('visible');
+}
+
+function showStats() {
+ stats.setMode(0); // 0: fps, 1: ms
+
+ // Align bottom-left.
+ stats.domElement.style.position = 'absolute';
+ stats.domElement.style.left = '0px';
+ stats.domElement.style.bottom = '0px';
+ document.body.appendChild(stats.domElement);
+}
+
+function loop(time) {
+ stats.begin();
+ renderer.render(time);
+ stats.end();
+ requestAnimationFrame(loop);
+}
+requestAnimationFrame(loop);
+
+},{"../node_modules/es6-promise/dist/es6-promise.min":1,"../node_modules/stats-js/build/stats.min":2,"../node_modules/webvr-polyfill/build/webvr-polyfill":7,"./device-motion-receiver":8,"./loading-indicator":11,"./photosphere-renderer":13,"./scene-loader":15,"./util":16}],13:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var Emitter = _dereq_('./emitter');
+var Eyes = _dereq_('./eyes');
+var THREE = _dereq_('../node_modules/three/three');
+THREE.VRControls = _dereq_('../node_modules/three/examples/js/controls/VRControls');
+THREE.VREffect = _dereq_('../node_modules/three/examples/js/effects/VREffect');
+var Util = _dereq_('./util');
+var VertexDistorter = _dereq_('./vertex-distorter');
+_dereq_('../node_modules/webvr-boilerplate/build/webvr-manager');
+
+function PhotosphereRenderer() {
+ this.init();
+}
+PhotosphereRenderer.prototype = new Emitter();
+
+PhotosphereRenderer.prototype.init = function() {
+ var container = document.querySelector('body');
+ var camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 100);
+ var cameraDummy = new THREE.Object3D();
+ cameraDummy.add(camera);
+
+ // Antialiasing temporarily disabled to improve performance.
+ var renderer = new THREE.WebGLRenderer({antialias: false});
+ renderer.setClearColor(0x000000, 0);
+ renderer.setSize(window.innerWidth, window.innerHeight);
+
+ // Round down fractional DPR values for better performance.
+ renderer.setPixelRatio(Math.floor(window.devicePixelRatio));
+ container.appendChild(renderer.domElement);
+
+ var controls = new THREE.VRControls(camera);
+ var effect = new THREE.VREffect(renderer);
+ effect.setSize(window.innerWidth, window.innerHeight);
+
+ this.camera = camera;
+ this.renderer = renderer;
+ this.effect = effect;
+ this.controls = controls;
+ this.manager = new WebVRManager(renderer, effect, {isUndistorted: true});
+
+ this.initScenes_();
+
+ // The vertex distorter.
+ this.distorter = new VertexDistorter(this.manager.getDeviceInfo());
+
+ this.manager.on('modechange', this.onModeChange_.bind(this));
+ this.manager.on('viewerchange', this.onViewerChange_.bind(this));
+
+ // Watch the resize event.
+ window.addEventListener('resize', this.onResize_.bind(this));
+
+ var that = this;
+ navigator.getVRDevices().then(function(devices) {
+ devices.forEach(function(device) {
+ if (device instanceof HMDVRDevice) {
+ that.hmd = device;
+ }
+ });
+ });
+};
+
+PhotosphereRenderer.prototype.render = function(timestamp) {
+ this.controls.update();
+ this.manager.render(this.scenes, this.camera, timestamp);
+};
+
+PhotosphereRenderer.prototype.setDefaultLookDirection = function(phi) {
+ // Rotate the camera parent to take into account the scene's rotation.
+ this.camera.parent.rotation.y = phi;
+};
+
+/**
+ * Sets the photosphere based on the image in the source. Supports stereo and
+ * mono photospheres.
+ *
+ * Emits 'load' and 'error' events.
+ */
+PhotosphereRenderer.prototype.setPhotosphere = function(src, opt_params) {
+ var params = opt_params || {};
+
+ this.isStereo = !!params.isStereo;
+ this.src = src;
+
+ // Load texture.
+ var loader = new THREE.TextureLoader();
+ loader.crossOrigin = 'anonymous';
+ loader.load(src, this.onTextureLoaded_.bind(this), null,
+ this.onTextureError_.bind(this));
+};
+
+PhotosphereRenderer.prototype.set360Video = function(videoElement, opt_params) {
+ var params = opt_params || {};
+
+ this.isStereo = !!params.isStereo;
+
+ // Load the video texture.
+ var videoTexture = new THREE.VideoTexture(videoElement);
+ videoTexture.minFilter = THREE.LinearFilter;
+ videoTexture.magFilter = THREE.LinearFilter;
+ videoTexture.format = THREE.RGBFormat;
+ videoTexture.generateMipmaps = false;
+ videoTexture.needsUpdate = true;
+
+ this.onTextureLoaded_(videoTexture);
+};
+
+PhotosphereRenderer.prototype.initScenes_ = function() {
+ this.sceneLeft = this.createScene_();
+ this.sceneRight = this.createScene_();
+ this.sceneLeft.add(this.camera.parent);
+
+ this.scenes = [this.sceneLeft, this.sceneRight];
+ this.eyes = [Eyes.LEFT, Eyes.RIGHT];
+};
+
+PhotosphereRenderer.prototype.onTextureLoaded_ = function(texture) {
+ var sphereLeft;
+ var sphereRight;
+ if (this.isStereo) {
+ sphereLeft = this.createPhotosphere_(texture, {offsetY: 0.5, scaleY: 0.5});
+ sphereRight = this.createPhotosphere_(texture, {offsetY: 0, scaleY: 0.5});
+ } else {
+ sphereLeft = this.createPhotosphere_(texture);
+ sphereRight = this.createPhotosphere_(texture);
+ }
+
+ this.sceneLeft.getObjectByName('photo').children = [sphereLeft];
+ this.sceneRight.getObjectByName('photo').children = [sphereRight];
+
+ this.emit('load');
+};
+
+PhotosphereRenderer.prototype.onTextureError_ = function(error) {
+ this.emit('error', 'Unable to load texture from ' + this.src);
+};
+
+
+PhotosphereRenderer.prototype.createPhotosphere_ = function(texture, opt_params) {
+ var p = opt_params || {};
+ p.scaleX = p.scaleX || 1;
+ p.scaleY = p.scaleY || 1;
+ p.offsetX = p.offsetX || 0;
+ p.offsetY = p.offsetY || 0;
+ p.phiStart = p.phiStart || 0;
+ p.phiLength = p.phiLength || Math.PI * 2;
+ p.thetaStart = p.thetaStart || 0;
+ p.thetaLength = p.thetaLength || Math.PI;
+
+ var geometry = new THREE.SphereGeometry(1, 48, 48,
+ p.phiStart, p.phiLength, p.thetaStart, p.thetaLength);
+ geometry.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
+ var uvs = geometry.faceVertexUvs[0];
+ for (var i = 0; i < uvs.length; i ++) {
+ for (var j = 0; j < 3; j ++) {
+ uvs[i][j].x *= p.scaleX;
+ uvs[i][j].x += p.offsetX;
+ uvs[i][j].y *= p.scaleY;
+ uvs[i][j].y += p.offsetY;
+ }
+ }
+
+ var material = new THREE.MeshBasicMaterial({ map: texture });
+ this.distorter.setMap(texture);
+ var out = new THREE.Mesh(geometry, material);
+ out.renderOrder = -1;
+ return out;
+};
+
+PhotosphereRenderer.prototype.createScene_ = function(opt_params) {
+ var scene = new THREE.Scene();
+ // Add a light.
+ scene.add(new THREE.PointLight(0xFFFFFF));
+
+ // Add a group for the photosphere.
+ var photoGroup = new THREE.Object3D();
+ photoGroup.name = 'photo';
+ scene.add(photoGroup);
+
+ return scene;
+};
+
+PhotosphereRenderer.prototype.updateMaterial_ = function(material_FOO) {
+ for (var i = 0; i < this.scenes.length; i++) {
+ var eye = this.eyes[i];
+ var material = this.distorter.getShaderMaterial(eye);
+ var scene = this.scenes[i];
+ var children = scene.getObjectByName('photo').children;
+ for (var j = 0; j < children.length; j++) {
+ var child = children[j];
+ child.material = material;
+ child.material.needsUpdate = true;
+ }
+ }
+};
+
+PhotosphereRenderer.prototype.updateRenderRect_ = function() {
+ if (this.hmd && this.hmd.setRenderRect) {
+ var deviceInfo = this.manager.getDeviceInfo();
+ var leftRect = deviceInfo.getUndistortedViewportLeftEye();
+ var dpr = window.devicePixelRatio;
+ leftRect.x /= dpr;
+ leftRect.y /= dpr;
+ leftRect.width /= dpr;
+ leftRect.height /= dpr;
+
+ var rightRect = Util.clone(leftRect);
+ rightRect.x = (window.innerWidth - leftRect.x) - leftRect.width;
+
+ this.hmd.setRenderRect(leftRect, rightRect);
+ }
+};
+
+PhotosphereRenderer.prototype.onModeChange_ = function(newMode, oldMode) {
+ console.log('onModeChange_', newMode);
+
+ var coefficients;
+ if (newMode == WebVRManager.Modes.VR) {
+ // Entering VR mode.
+ this.distorter.setEnabled(true);
+ this.updateMaterial_();
+ } else if (oldMode == WebVRManager.Modes.VR) {
+ // Leaving VR mode.
+ this.distorter.setEnabled(false);
+ this.updateMaterial_();
+ }
+
+ if (window.analytics) {
+ analytics.logModeChanged(newMode);
+ }
+};
+
+PhotosphereRenderer.prototype.onViewerChange_ = function(newViewer) {
+ console.log('onViewerChange_', newViewer);
+
+ // Reset the photosphere with new coefficients.
+ this.updateMaterial_();
+ this.updateRenderRect_();
+};
+
+PhotosphereRenderer.prototype.onResize_ = function() {
+ this.updateRenderRect_();
+};
+
+module.exports = PhotosphereRenderer;
+
+},{"../node_modules/three/examples/js/controls/VRControls":3,"../node_modules/three/examples/js/effects/VREffect":4,"../node_modules/three/three":5,"../node_modules/webvr-boilerplate/build/webvr-manager":6,"./emitter":9,"./eyes":10,"./util":16,"./vertex-distorter":17}],14:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Contains all information about a given scene, including the photosphere asset,
+ * background music.
+ */
+function SceneInfo(opt_params) {
+ var params = opt_params || {};
+ this.id = params.id;
+ this.title = params.title;
+ this.image = params.image;
+ this.preview = params.preview;
+ this.isStereo = !!params.isStereo;
+ this.audio = params.audio;
+ this.video = params.video;
+ this.yaw = params.yaw || 0;
+ this.isYawOnly = params.isYawOnly;
+}
+
+SceneInfo.prototype.isComplete = function() {
+ return !!this.image || !!this.video;
+};
+
+SceneInfo.prototype.toObject = function() {
+ return {
+ id: this.id || null,
+ title: this.title || null,
+ image: this.image,
+ preview: this.preview,
+ isStereo: this.isStereo,
+ audio: this.audio,
+ yaw: this.yaw || null,
+ video: this.video || null,
+ };
+};
+
+SceneInfo.prototype.isImageDataURI = function() {
+ return this.image.indexOf('data:') == 0;
+};
+
+
+module.exports = SceneInfo;
+
+},{}],15:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var Emitter = _dereq_('./emitter');
+var SceneInfo = _dereq_('./scene-info');
+
+var Query = {
+ JSON_URL: 'url',
+ VIDEO_URL: 'video',
+ OBJECT_URL: 'object',
+ IMAGE_URL: 'image',
+ PREVIEW_URL: 'preview',
+ IS_STEREO: 'is_stereo',
+ AUDIO_URL: 'audio',
+ START_YAW: 'start_yaw',
+ IS_YAW_ONLY: 'is_yaw_only',
+};
+
+function SceneLoader() {
+}
+SceneLoader.prototype = new Emitter();
+
+/**
+ * Loads a scene from a JSON file.
+ */
+SceneLoader.prototype.loadFromJson_ = function(url, callback) {
+ // XHR to fetch the JSON.
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url);
+ var that = this;
+ xhr.onload = function(e) {
+ try {
+ var jsonObj = JSON.parse(this.response);
+ } catch (e) {
+ that.emit('error', 'Invalid JSON at ' + url + '.');
+ return;
+ }
+ var labelObjects = {};
+ var labels = jsonObj.labels || [];
+ // Go through the labels in the data and objectify them.
+ for (var i = 0; i < labels.length; i++) {
+ var label = new Label(labels[i]);
+ labelObjects[label.id] = label;
+ }
+ jsonObj.labels = labelObjects;
+ var scene = new SceneInfo(jsonObj);
+ that.emit('load', scene);
+ };
+ xhr.send();
+};
+
+/**
+ * Parse out GET parameters from the URL string and load the scene.
+ */
+SceneLoader.prototype.loadFromGetParams_ = function() {
+ var params = {
+ image: Util.getQueryParameter(Query.IMAGE_URL),
+ video: Util.getQueryParameter(Query.VIDEO_URL),
+ object: Util.getQueryParameter(Query.OBJECT_URL),
+ preview: Util.getQueryParameter(Query.PREVIEW_URL),
+ isStereo: this.parseBoolean_(Util.getQueryParameter(Query.IS_STEREO)),
+ audio: Util.getQueryParameter(Query.AUDIO_URL),
+ isYawOnly: this.parseBoolean_(Util.getQueryParameter(Query.IS_YAW_ONLY)),
+ yaw: THREE.Math.degToRad(Util.getQueryParameter(Query.START_YAW)),
+ };
+
+ var count = 0;
+ count += (params[Query.IMAGE_URL] ? 1 : 0);
+ count += (params[Query.VIDEO_URL] ? 1 : 0);
+ count += (params[Query.OBJECT_URL] ? 1 : 0);
+ // Validate this.
+ if (count == 0) {
+ this.emit('error', 'Either "image", "video", or "object" GET parameter is required.');
+ return;
+ }
+
+ var scene = new SceneInfo(params);
+ this.emit('load', scene);
+};
+
+SceneLoader.prototype.parseBoolean_ = function(value) {
+ if (value == 'false') {
+ return false;
+ }
+ return !!value;
+};
+
+SceneLoader.prototype.loadScene = function(callback) {
+ // If there's a url param specified, try loading from JSON.
+ var url = Util.getQueryParameter('url');
+ var image = Util.getQueryParameter('image');
+ var video = Util.getQueryParameter('video');
+ var object = Util.getQueryParameter('object');
+ if (url) {
+ this.loadFromJson_(url);
+ } else if (image || video || object) {
+ // Otherwise, try loading from URL parameters.
+ this.loadFromGetParams_();
+ } else {
+ // If it fails, throw an exception.
+ this.emit('error', 'Unable to load scene. Required parameter missing.');
+ }
+};
+
+module.exports = SceneLoader;
+
+},{"./emitter":9,"./scene-info":14}],16:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+Util = window.Util || {};
+
+Util.isDataURI = function(src) {
+ return src && src.indexOf('data:') == 0;
+};
+
+Util.generateUUID = function() {
+ function s4() {
+ return Math.floor((1 + Math.random()) * 0x10000)
+ .toString(16)
+ .substring(1);
+ }
+ return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
+ s4() + '-' + s4() + s4() + s4();
+};
+
+Util.isMobile = function() {
+ var check = false;
+ (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
+ return check;
+};
+
+Util.isIOS = function() {
+ return /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
+};
+
+Util.cloneObject = function(obj) {
+ var out = {};
+ for (key in obj) {
+ out[key] = obj[key];
+ }
+ return out;
+};
+
+Util.hashCode = function(s) {
+ return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);
+};
+
+Util.loadTrackSrc = function(context, src, callback, opt_progressCallback) {
+ var request = new XMLHttpRequest();
+ request.open('GET', src, true);
+ request.responseType = 'arraybuffer';
+
+ // Decode asynchronously.
+ request.onload = function() {
+ context.decodeAudioData(request.response, function(buffer) {
+ callback(buffer);
+ }, function(e) {
+ console.error(e);
+ });
+ };
+ if (opt_progressCallback) {
+ request.onprogress = function(e) {
+ var percent = e.loaded / e.total;
+ opt_progressCallback(percent);
+ };
+ }
+ request.send();
+};
+
+Util.isPow2 = function(n) {
+ return (n & (n - 1)) == 0;
+};
+
+Util.capitalize = function(s) {
+ return s.charAt(0).toUpperCase() + s.slice(1);
+};
+
+Util.isIFrame = function() {
+ try {
+ return window.self !== window.top;
+ } catch (e) {
+ return true;
+ }
+};
+
+// From http://goo.gl/4WX3tg
+Util.getQueryParameter = function(name) {
+ name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
+ var regex = new RegExp("[\\?&]" + name + "=([^]*)"),
+ results = regex.exec(location.search);
+ return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
+};
+
+
+// From http://stackoverflow.com/questions/11871077/proper-way-to-detect-webgl-support.
+Util.isWebGLEnabled = function() {
+ var canvas = document.createElement('canvas');
+ try { gl = canvas.getContext("webgl"); }
+ catch (x) { gl = null; }
+
+ if (gl == null) {
+ try { gl = canvas.getContext("experimental-webgl"); experimental = true; }
+ catch (x) { gl = null; }
+ }
+ return !!gl;
+};
+
+Util.clone = function(obj) {
+ return JSON.parse(JSON.stringify(obj));
+};
+
+// From http://stackoverflow.com/questions/10140604/fastest-hypotenuse-in-javascript
+Util.hypot = Math.hypot || function(x, y) {
+ return Math.sqrt(x*x + y*y);
+};
+
+// From http://stackoverflow.com/a/17447718/693934
+Util.isIE11 = function() {
+ return navigator.userAgent.match(/Trident/);
+};
+
+module.exports = Util;
+
+},{}],17:[function(_dereq_,module,exports){
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+THREE = _dereq_('../node_modules/three/three');
+var Eyes = _dereq_('./eyes');
+var Util = _dereq_('./util');
+
+var DEFAULT_FOV = 40;
+var NO_DISTORTION = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+/**
+ * Responsible for distortion correction by pre-distorting the vertex geometry
+ * so that a second shader pass is not needed. This is intended to greatly
+ * optimize the rendering process.
+ */
+function VertexDistorter(deviceInfo) {
+ this.texture = null;
+ this.isEnabled = false;
+
+ this.deviceInfo = deviceInfo;
+}
+
+VertexDistorter.prototype.setEnabled = function(isEnabled) {
+ this.isEnabled = isEnabled;
+}
+
+/**
+ * Sets the texture that is used to render this photosphere.
+ */
+VertexDistorter.prototype.setMap = function(texture) {
+ this.texture = texture;
+};
+
+VertexDistorter.prototype.getVertexShader_ = function() {
+ return [
+ '#ifdef GL_ES',
+ 'precision highp float;',
+ '#endif',
+
+ 'varying vec2 vUV;',
+
+ this.getDistortionInclude_(),
+
+ 'void main() {',
+ // Pass through texture coordinates to the fragment shader.
+ 'vUV = uv;',
+
+ // Here, we want to ensure that we are using an undistorted projection
+ // matrix. By setting isUndistorted: true in the WebVRManager, we
+ // guarantee this.
+ 'vec4 pos = projectionMatrix * modelViewMatrix * vec4(position, 1.0);',
+
+ 'gl_Position = Distort(pos);',
+ '}'
+ ].join('\n');
+};
+
+VertexDistorter.prototype.getNoopVertexShader_ = function() {
+ return [
+ '#ifdef GL_ES',
+ 'precision highp float;',
+ '#endif',
+
+ 'varying vec2 vUV;',
+
+ 'void main() {',
+ 'gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);',
+ 'vUV = uv;',
+ '}'
+ ].join('\n');
+};
+
+VertexDistorter.prototype.getFragmentShader_ = function() {
+ return [
+ '#ifdef GL_ES',
+ 'precision highp float;',
+ '#endif',
+
+ 'varying vec2 vUV;',
+ 'uniform sampler2D texture;',
+
+ 'void main() {',
+ 'gl_FragColor = texture2D(texture, vUV);',
+ '}'
+ ].join('\n');
+};
+
+VertexDistorter.prototype.getNoopDistortionInclude_ = function() {
+ return [
+ 'vec4 Distort(vec4 point) {',
+ 'return point;',
+ '}'
+ ].join('\n');
+};
+
+VertexDistorter.prototype.getDistortionInclude_ = function() {
+ return [
+ 'uniform float uDistortionCoefficients[12];',
+ 'uniform float uDistortionMaxFovSquared;',
+ 'uniform vec2 uDistortionFovOffset;',
+ 'uniform vec2 uDistortionFovScale;',
+
+ // Returns a scalar to distort a point; computed in reverse via the polynomial approximation:
+ // r' = 1 + Σ_i (uDistortionCoefficients[i] rSquared^(i+1)) i=[0..11]
+ // where rSquared is the squared radius of an undistorted point in tan-angle space.
+ // See {@link Distortion} for more information.
+ 'float DistortionFactor(float rSquared) {',
+ 'float ret = 0.0;',
+ 'rSquared = min(uDistortionMaxFovSquared, rSquared);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[11]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[10]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[9]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[8]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[7]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[6]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[5]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[4]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[3]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[2]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[1]);',
+ 'ret = rSquared * (ret + uDistortionCoefficients[0]);',
+ 'return ret + 1.0;',
+ '}',
+
+ // Given a point in clip space, distort the point according to the coefficients stored in
+ // uDistortionCoefficients and the field of view (FOV) specified in uDistortionFovOffset and
+ // uDistortionFovScale.
+ // Returns the distorted point in clip space, with its Z untouched.
+ 'vec4 Distort(vec4 point) {',
+ // Put point into normalized device coordinates (NDC), [(-1, -1, -1) to (1, 1, 1)].
+ 'vec3 pointNdc = point.xyz / point.w;',
+ // Throw away the Z coordinate and map the point to the unit square, [(0, 0) to (1, 1)].
+ 'vec2 pointUnitSquare = (pointNdc.xy + vec2(1.0)) / 2.0;',
+ // Map the point into FOV tan-angle space.
+ 'vec2 pointTanAngle = pointUnitSquare * uDistortionFovScale - uDistortionFovOffset;',
+ 'float radiusSquared = dot(pointTanAngle, pointTanAngle);',
+ 'float distortionFactor = DistortionFactor(radiusSquared);',
+ //'float distortionFactor = 2.0;',
+ 'vec2 distortedPointTanAngle = pointTanAngle * distortionFactor;',
+ // Reverse the mappings above to bring the distorted point back into NDC space.
+ 'vec2 distortedPointUnitSquare = (distortedPointTanAngle + uDistortionFovOffset)',
+ '/ uDistortionFovScale;',
+ 'vec3 distortedPointNdc = vec3(distortedPointUnitSquare * 2.0 - vec2(1.0), pointNdc.z);',
+ // Convert the point into clip space before returning in case any operations are done after.
+ 'return vec4(distortedPointNdc, 1.0) * point.w;',
+ '}',
+ ].join('\n');
+};
+
+VertexDistorter.prototype.getDistortionMaxFovSquared_ = function() {
+ var fov = this.getFov_();
+ var maxFov = Util.hypot(
+ Math.tan(THREE.Math.degToRad(Math.max(fov.leftDegrees, fov.rightDegrees))),
+ Math.tan(THREE.Math.degToRad(Math.max(fov.downDegrees, fov.upDegrees))));
+ return maxFov * maxFov;
+};
+
+VertexDistorter.prototype.getDistortionCoefficients_ = function() {
+ var viewer = this.deviceInfo.viewer;
+ return this.isEnabled ? viewer.inverseCoefficients : NO_DISTORTION;
+};
+
+VertexDistorter.prototype.getDistortionFovOffset_ = function(eye) {
+ var fov = this.getFov_(eye);
+ /*
+ var sideDegrees = Math.min(fov.leftDegrees, fov.rightDegrees);
+ var left = Math.tan(THREE.Math.degToRad(sideDegrees));
+ */
+ var left = Math.tan(THREE.Math.degToRad(fov.leftDegrees));
+ var down = Math.tan(THREE.Math.degToRad(fov.downDegrees));
+ return new THREE.Vector2(left, down);
+};
+
+VertexDistorter.prototype.getDistortionFovScale_ = function() {
+ var fov = this.getFov_();
+ var left = Math.tan(THREE.Math.degToRad(fov.leftDegrees));
+ var right = Math.tan(THREE.Math.degToRad(fov.rightDegrees));
+ var up = Math.tan(THREE.Math.degToRad(fov.upDegrees));
+ var down = Math.tan(THREE.Math.degToRad(fov.downDegrees));
+ return new THREE.Vector2(left + right, up + down);
+};
+
+
+VertexDistorter.prototype.getUniforms_ = function(eye) {
+ return {
+ texture: {
+ type: 't',
+ value: this.texture
+ },
+ uDistortionCoefficients: {
+ type: 'fv1',
+ value: this.getDistortionCoefficients_()
+ },
+ uDistortionMaxFovSquared: {
+ type: 'f',
+ value: this.getDistortionMaxFovSquared_()
+ },
+ uDistortionFovOffset: {
+ type: 'v2',
+ value: this.getDistortionFovOffset_(eye)
+ },
+ uDistortionFovScale: {
+ type: 'v2',
+ value: this.getDistortionFovScale_()
+ }
+ };
+};
+
+VertexDistorter.prototype.getShaderMaterial = function(eye) {
+ var uniforms = this.getUniforms_(eye);
+ return new THREE.ShaderMaterial({
+ uniforms: uniforms,
+ vertexShader: this.getVertexShader_(),
+ fragmentShader: this.getFragmentShader_()
+ });
+};
+
+VertexDistorter.prototype.getFov_ = function(opt_eye) {
+ var eye = opt_eye || Eyes.LEFT;
+
+ if (eye == Eyes.LEFT) {
+ return this.deviceInfo.getFieldOfViewLeftEye(true);
+ }
+ if (eye == Eyes.RIGHT) {
+ return this.deviceInfo.getFieldOfViewRightEye(true);
+ }
+ return null;
+};
+
+module.exports = VertexDistorter;
+
+},{"../node_modules/three/three":5,"./eyes":10,"./util":16}],18:[function(_dereq_,module,exports){
+// shim for using process in browser
+
+var process = module.exports = {};
+var queue = [];
+var draining = false;
+var currentQueue;
+var queueIndex = -1;
+
+function cleanUpNextTick() {
+ draining = false;
+ if (currentQueue.length) {
+ queue = currentQueue.concat(queue);
+ } else {
+ queueIndex = -1;
+ }
+ if (queue.length) {
+ drainQueue();
+ }
+}
+
+function drainQueue() {
+ if (draining) {
+ return;
+ }
+ var timeout = setTimeout(cleanUpNextTick);
+ draining = true;
+
+ var len = queue.length;
+ while(len) {
+ currentQueue = queue;
+ queue = [];
+ while (++queueIndex < len) {
+ if (currentQueue) {
+ currentQueue[queueIndex].run();
+ }
+ }
+ queueIndex = -1;
+ len = queue.length;
+ }
+ currentQueue = null;
+ draining = false;
+ clearTimeout(timeout);
+}
+
+process.nextTick = function (fun) {
+ var args = new Array(arguments.length - 1);
+ if (arguments.length > 1) {
+ for (var i = 1; i < arguments.length; i++) {
+ args[i - 1] = arguments[i];
+ }
+ }
+ queue.push(new Item(fun, args));
+ if (queue.length === 1 && !draining) {
+ setTimeout(drainQueue, 0);
+ }
+};
+
+// v8 likes predictible objects
+function Item(fun, array) {
+ this.fun = fun;
+ this.array = array;
+}
+Item.prototype.run = function () {
+ this.fun.apply(null, this.array);
+};
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
+};
+
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+},{}]},{},[12]);
diff --git a/build/vrview.min.js b/build/vrview.min.js
new file mode 100644
index 00000000..efea002d
--- /dev/null
+++ b/build/vrview.min.js
@@ -0,0 +1,22 @@
+!function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;ot;t+=2){var e=X[t],n=X[t+1];e(n),X[t]=void 0,X[t+1]=void 0}G=0}function f(){try{var t=_dereq_,e=t("vertx");return U=e.runOnLoop||e.runOnContext,i()}catch(n){return c()}}function l(t,e){var n=this,r=n._state;if(r===et&&!t||r===nt&&!e)return this;var o=new this.constructor(p),i=n._result;if(r){var s=arguments[r-1];H(function(){C(r,o,s,i)})}else j(n,o,t,e);return o}function h(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(p);return g(n,t),n}function p(){}function _(){return new TypeError("You cannot resolve a promise with itself")}function v(){return new TypeError("A promises callback cannot return that same promise.")}function d(t){try{return t.then}catch(e){return rt.error=e,rt}}function y(t,e,n,r){try{t.call(e,n,r)}catch(o){return o}}function m(t,e,n){H(function(t){var r=!1,o=y(n,e,function(n){r||(r=!0,e!==n?g(t,n):E(t,n))},function(e){r||(r=!0,S(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&o&&(r=!0,S(t,o))},t)}function w(t,e){e._state===et?E(t,e._result):e._state===nt?S(t,e._result):j(e,void 0,function(e){g(t,e)},function(e){S(t,e)})}function b(t,n,r){n.constructor===t.constructor&&r===Z&&constructor.resolve===$?w(t,n):r===rt?S(t,rt.error):void 0===r?E(t,n):e(r)?m(t,n,r):E(t,n)}function g(e,n){e===n?S(e,_()):t(n)?b(e,n,d(n)):E(e,n)}function A(t){t._onerror&&t._onerror(t._result),T(t)}function E(t,e){t._state===tt&&(t._result=e,t._state=et,0!==t._subscribers.length&&H(T,t))}function S(t,e){t._state===tt&&(t._state=nt,t._result=e,H(A,t))}function j(t,e,n,r){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+et]=n,o[i+nt]=r,0===i&&t._state&&H(T,t)}function T(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,o,i=t._result,s=0;ss;s++)j(r.resolve(t[s]),void 0,e,n);return o}function Y(t){var e=this,n=new e(p);return S(n,t),n}function q(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function F(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function D(t){this._id=ct++,this._state=void 0,this._result=void 0,this._subscribers=[],p!==t&&("function"!=typeof t&&q(),this instanceof D?M(this,t):F())}function K(t,e){this._instanceConstructor=t,this.promise=new t(p),Array.isArray(e)?(this._input=e,this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?E(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&E(this.promise,this._result))):S(this.promise,this._validationError())}function L(){var t;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;(!n||"[object Promise]"!==Object.prototype.toString.call(n.resolve())||n.cast)&&(t.Promise=at)}var N;N=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var U,W,z,B=N,G=0,H=function(t,e){X[G]=t,X[G+1]=e,G+=2,2===G&&(W?W(a):z())},I="undefined"!=typeof window?window:void 0,J=I||{},Q=J.MutationObserver||J.WebKitMutationObserver,R="undefined"!=typeof process&&"[object process]"==={}.toString.call(process),V="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,X=new Array(1e3);z=R?o():Q?s():V?u():void 0===I&&"function"==typeof _dereq_?f():c();var Z=l,$=h,tt=void 0,et=1,nt=2,rt=new P,ot=new P,it=O,st=k,ut=Y,ct=0,at=D;D.all=it,D.race=st,D.resolve=$,D.reject=ut,D._setScheduler=n,D._setAsap=r,D._asap=H,D.prototype={constructor:D,then:Z,"catch":function(t){return this.then(null,t)}};var ft=K;K.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},K.prototype._enumerate=function(){for(var t=this.length,e=this._input,n=0;this._state===tt&&t>n;n++)this._eachEntry(e[n],n)},K.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===$){var o=d(t);if(o===Z&&t._state!==tt)this._settledAt(t._state,e,t._result);else if("function"!=typeof o)this._remaining--,this._result[e]=t;else if(n===at){var i=new n(p);b(i,t,o),this._willSettleAt(i,e)}else this._willSettleAt(new n(function(e){e(t)}),e)}else this._willSettleAt(r(t),e)},K.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===tt&&(this._remaining--,t===nt?S(r,n):this._result[e]=n),0===this._remaining&&E(r,this._result)},K.prototype._willSettleAt=function(t,e){var n=this;j(t,void 0,function(t){n._settledAt(et,e,t)},function(t){n._settledAt(nt,e,t)})};var lt=L,ht={Promise:at,polyfill:lt};"function"==typeof define&&define.amd?define(function(){return ht}):"undefined"!=typeof module&&module.exports?module.exports=ht:"undefined"!=typeof this&&(this.ES6Promise=ht),lt()}).call(this)}).call(this,_dereq_("_process"),"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{_process:18}],2:[function(_dereq_,module,exports){var Stats=function(){var l=Date.now(),m=l,g=0,n=1/0,o=0,h=0,p=1/0,q=0,r=0,s=0,f=document.createElement("div");f.id="stats",f.addEventListener("mousedown",function(b){b.preventDefault(),t(++s%2)},!1),f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps",a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002",f.appendChild(a);var i=document.createElement("div");i.id="fpsText",i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px",i.innerHTML="FPS",a.appendChild(i);var c=document.createElement("div");for(c.id="fpsGraph",c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff",a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113",c.appendChild(j)}var d=document.createElement("div");d.id="ms",d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none",f.appendChild(d);var k=document.createElement("div");k.id="msText",k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px",k.innerHTML="MS",d.appendChild(k);var e=document.createElement("div");for(e.id="msGraph",e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0",d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){switch(s=b){case 0:a.style.display="block",d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:12,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l,n=Math.min(n,g),o=Math.max(o,g),k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));return e.appendChild(e.firstChild).style.height=a+"px",r++,b>m+1e3&&(h=Math.round(1e3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height=a+"px",m=b,r=0),b},update:function(){l=this.end()}}};"object"==typeof module&&(module.exports=Stats)},{}],3:[function(_dereq_,module,exports){var VRControls=function(object,onError){function filterInvalidDevices(devices){var oculusDevices=devices.filter(function(device){return-1!==device.deviceName.toLowerCase().indexOf("oculus")});return oculusDevices.length>=1?devices.filter(function(device){return-1===device.deviceName.toLowerCase().indexOf("cardboard")}):devices}function gotVRDevices(devices){devices=filterInvalidDevices(devices);for(var i=0;ix?-1:x>0?1:+x}),void 0===Function.prototype.name&&void 0!==Object.defineProperty&&Object.defineProperty(Function.prototype,"name",{get:function(){return this.toString().match(/^\s*function\s*(\S*)\s*\(/)[1]}}),THREE.MOUSE={LEFT:0,MIDDLE:1,RIGHT:2},THREE.CullFaceNone=0,THREE.CullFaceBack=1,THREE.CullFaceFront=2,THREE.CullFaceFrontBack=3,THREE.FrontFaceDirectionCW=0,THREE.FrontFaceDirectionCCW=1,THREE.BasicShadowMap=0,THREE.PCFShadowMap=1,THREE.PCFSoftShadowMap=2,THREE.FrontSide=0,THREE.BackSide=1,THREE.DoubleSide=2,THREE.FlatShading=1,THREE.SmoothShading=2,THREE.NoColors=0,THREE.FaceColors=1,THREE.VertexColors=2,THREE.NoBlending=0,THREE.NormalBlending=1,THREE.AdditiveBlending=2,THREE.SubtractiveBlending=3,THREE.MultiplyBlending=4,THREE.CustomBlending=5,THREE.AddEquation=100,THREE.SubtractEquation=101,THREE.ReverseSubtractEquation=102,THREE.MinEquation=103,THREE.MaxEquation=104,THREE.ZeroFactor=200,THREE.OneFactor=201,THREE.SrcColorFactor=202,THREE.OneMinusSrcColorFactor=203,THREE.SrcAlphaFactor=204,THREE.OneMinusSrcAlphaFactor=205,THREE.DstAlphaFactor=206,THREE.OneMinusDstAlphaFactor=207,THREE.DstColorFactor=208,THREE.OneMinusDstColorFactor=209,THREE.SrcAlphaSaturateFactor=210,THREE.NeverDepth=0,THREE.AlwaysDepth=1,THREE.LessDepth=2,THREE.LessEqualDepth=3,THREE.EqualDepth=4,THREE.GreaterEqualDepth=5,THREE.GreaterDepth=6,THREE.NotEqualDepth=7,THREE.MultiplyOperation=0,THREE.MixOperation=1,THREE.AddOperation=2,THREE.UVMapping=300,THREE.CubeReflectionMapping=301,THREE.CubeRefractionMapping=302,THREE.EquirectangularReflectionMapping=303,THREE.EquirectangularRefractionMapping=304,THREE.SphericalReflectionMapping=305,THREE.RepeatWrapping=1e3,THREE.ClampToEdgeWrapping=1001,THREE.MirroredRepeatWrapping=1002,THREE.NearestFilter=1003,THREE.NearestMipMapNearestFilter=1004,THREE.NearestMipMapLinearFilter=1005,THREE.LinearFilter=1006,THREE.LinearMipMapNearestFilter=1007,THREE.LinearMipMapLinearFilter=1008,THREE.UnsignedByteType=1009,THREE.ByteType=1010,THREE.ShortType=1011,THREE.UnsignedShortType=1012,THREE.IntType=1013,THREE.UnsignedIntType=1014,THREE.FloatType=1015,THREE.HalfFloatType=1025,THREE.UnsignedShort4444Type=1016,THREE.UnsignedShort5551Type=1017,THREE.UnsignedShort565Type=1018,THREE.AlphaFormat=1019,THREE.RGBFormat=1020,THREE.RGBAFormat=1021,THREE.LuminanceFormat=1022,THREE.LuminanceAlphaFormat=1023,THREE.RGBEFormat=THREE.RGBAFormat,THREE.RGB_S3TC_DXT1_Format=2001,THREE.RGBA_S3TC_DXT1_Format=2002,THREE.RGBA_S3TC_DXT3_Format=2003,THREE.RGBA_S3TC_DXT5_Format=2004,THREE.RGB_PVRTC_4BPPV1_Format=2100,THREE.RGB_PVRTC_2BPPV1_Format=2101,THREE.RGBA_PVRTC_4BPPV1_Format=2102,THREE.RGBA_PVRTC_2BPPV1_Format=2103,THREE.LoopOnce=2200,THREE.LoopRepeat=2201,THREE.LoopPingPong=2202,THREE.Projector=function(){console.error("THREE.Projector has been moved to /examples/js/renderers/Projector.js."),this.projectVector=function(vector,camera){console.warn("THREE.Projector: .projectVector() is now vector.project()."),vector.project(camera)},this.unprojectVector=function(vector,camera){console.warn("THREE.Projector: .unprojectVector() is now vector.unproject()."),vector.unproject(camera)},this.pickingRay=function(vector,camera){console.error("THREE.Projector: .pickingRay() is now raycaster.setFromCamera().")}},THREE.CanvasRenderer=function(){console.error("THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js"),this.domElement=document.createElement("canvas"),this.clear=function(){},this.render=function(){},this.setClearColor=function(){},this.setSize=function(){}},THREE.Color=function(color){return 3===arguments.length?this.fromArray(arguments):this.set(color)},THREE.Color.prototype={constructor:THREE.Color,r:1,g:1,b:1,set:function(value){return value instanceof THREE.Color?this.copy(value):"number"==typeof value?this.setHex(value):"string"==typeof value&&this.setStyle(value),this},setHex:function(hex){return hex=Math.floor(hex),this.r=(hex>>16&255)/255,this.g=(hex>>8&255)/255,this.b=(255&hex)/255,this},setRGB:function(r,g,b){return this.r=r,this.g=g,this.b=b,this},setHSL:function(){function hue2rgb(p,q,t){return 0>t&&(t+=1),t>1&&(t-=1),1/6>t?p+6*(q-p)*t:.5>t?q:2/3>t?p+6*(q-p)*(2/3-t):p}return function(h,s,l){if(h=THREE.Math.euclideanModulo(h,1),s=THREE.Math.clamp(s,0,1),l=THREE.Math.clamp(l,0,1),0===s)this.r=this.g=this.b=l;else{var p=.5>=l?l*(1+s):l+s-l*s,q=2*l-p;this.r=hue2rgb(q,p,h+1/3),this.g=hue2rgb(q,p,h),this.b=hue2rgb(q,p,h-1/3)}return this}}(),setStyle:function(style){function handleAlpha(string){void 0!==string&&parseFloat(string)<1&&console.warn("THREE.Color: Alpha component of "+style+" will be ignored.")}var m;if(m=/^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec(style)){var color,name=m[1],components=m[2];switch(name){case"rgb":case"rgba":if(color=/^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(components))return this.r=Math.min(255,parseInt(color[1],10))/255,this.g=Math.min(255,parseInt(color[2],10))/255,this.b=Math.min(255,parseInt(color[3],10))/255,handleAlpha(color[5]),this;if(color=/^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(components))return this.r=Math.min(100,parseInt(color[1],10))/100,this.g=Math.min(100,parseInt(color[2],10))/100,this.b=Math.min(100,parseInt(color[3],10))/100,handleAlpha(color[5]),this;break;case"hsl":case"hsla":if(color=/^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(components)){var h=parseFloat(color[1])/360,s=parseInt(color[2],10)/100,l=parseInt(color[3],10)/100;return handleAlpha(color[5]),this.setHSL(h,s,l)}}}else if(m=/^\#([A-Fa-f0-9]+)$/.exec(style)){var hex=m[1],size=hex.length;if(3===size)return this.r=parseInt(hex.charAt(0)+hex.charAt(0),16)/255,this.g=parseInt(hex.charAt(1)+hex.charAt(1),16)/255,this.b=parseInt(hex.charAt(2)+hex.charAt(2),16)/255,this;if(6===size)return this.r=parseInt(hex.charAt(0)+hex.charAt(1),16)/255,this.g=parseInt(hex.charAt(2)+hex.charAt(3),16)/255,this.b=parseInt(hex.charAt(4)+hex.charAt(5),16)/255,this}if(style&&style.length>0){var hex=THREE.ColorKeywords[style];void 0!==hex?this.setHex(hex):console.warn("THREE.Color: Unknown color "+style)}return this},clone:function(){return new this.constructor(this.r,this.g,this.b)},copy:function(color){return this.r=color.r,this.g=color.g,this.b=color.b,this},copyGammaToLinear:function(color,gammaFactor){return void 0===gammaFactor&&(gammaFactor=2),this.r=Math.pow(color.r,gammaFactor),this.g=Math.pow(color.g,gammaFactor),this.b=Math.pow(color.b,gammaFactor),this},copyLinearToGamma:function(color,gammaFactor){void 0===gammaFactor&&(gammaFactor=2);var safeInverse=gammaFactor>0?1/gammaFactor:1;return this.r=Math.pow(color.r,safeInverse),this.g=Math.pow(color.g,safeInverse),this.b=Math.pow(color.b,safeInverse),this},convertGammaToLinear:function(){var r=this.r,g=this.g,b=this.b;return this.r=r*r,this.g=g*g,this.b=b*b,this},convertLinearToGamma:function(){return this.r=Math.sqrt(this.r),this.g=Math.sqrt(this.g),this.b=Math.sqrt(this.b),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(optionalTarget){var hue,saturation,hsl=optionalTarget||{h:0,s:0,l:0},r=this.r,g=this.g,b=this.b,max=Math.max(r,g,b),min=Math.min(r,g,b),lightness=(min+max)/2;if(min===max)hue=0,saturation=0;else{var delta=max-min;switch(saturation=.5>=lightness?delta/(max+min):delta/(2-max-min),max){case r:hue=(g-b)/delta+(b>g?6:0);break;case g:hue=(b-r)/delta+2;break;case b:hue=(r-g)/delta+4}hue/=6}return hsl.h=hue,hsl.s=saturation,hsl.l=lightness,hsl},getStyle:function(){return"rgb("+(255*this.r|0)+","+(255*this.g|0)+","+(255*this.b|0)+")"},offsetHSL:function(h,s,l){var hsl=this.getHSL();return hsl.h+=h,hsl.s+=s,hsl.l+=l,this.setHSL(hsl.h,hsl.s,hsl.l),this},add:function(color){return this.r+=color.r,this.g+=color.g,this.b+=color.b,this},addColors:function(color1,color2){return this.r=color1.r+color2.r,this.g=color1.g+color2.g,this.b=color1.b+color2.b,this},addScalar:function(s){return this.r+=s,this.g+=s,this.b+=s,this},multiply:function(color){return this.r*=color.r,this.g*=color.g,this.b*=color.b,this},multiplyScalar:function(s){return this.r*=s,this.g*=s,this.b*=s,this},lerp:function(color,alpha){return this.r+=(color.r-this.r)*alpha,this.g+=(color.g-this.g)*alpha,this.b+=(color.b-this.b)*alpha,this},equals:function(c){return c.r===this.r&&c.g===this.g&&c.b===this.b},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.r=array[offset],this.g=array[offset+1],this.b=array[offset+2],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.r,array[offset+1]=this.g,array[offset+2]=this.b,array}},THREE.ColorKeywords={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},THREE.Quaternion=function(x,y,z,w){this._x=x||0,this._y=y||0,this._z=z||0,this._w=void 0!==w?w:1},THREE.Quaternion.prototype={constructor:THREE.Quaternion,get x(){return this._x},set x(value){this._x=value,this.onChangeCallback()},get y(){return this._y},set y(value){this._y=value,this.onChangeCallback()},get z(){return this._z},set z(value){this._z=value,this.onChangeCallback()},get w(){return this._w},set w(value){this._w=value,this.onChangeCallback()},set:function(x,y,z,w){return this._x=x,this._y=y,this._z=z,this._w=w,this.onChangeCallback(),this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._w)},copy:function(quaternion){return this._x=quaternion.x,this._y=quaternion.y,this._z=quaternion.z,this._w=quaternion.w,this.onChangeCallback(),this},setFromEuler:function(euler,update){if(euler instanceof THREE.Euler==!1)throw new Error("THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var c1=Math.cos(euler._x/2),c2=Math.cos(euler._y/2),c3=Math.cos(euler._z/2),s1=Math.sin(euler._x/2),s2=Math.sin(euler._y/2),s3=Math.sin(euler._z/2),order=euler.order;return"XYZ"===order?(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):"YXZ"===order?(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):"ZXY"===order?(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):"ZYX"===order?(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):"YZX"===order?(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):"XZY"===order&&(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),update!==!1&&this.onChangeCallback(),this},setFromAxisAngle:function(axis,angle){var halfAngle=angle/2,s=Math.sin(halfAngle);return this._x=axis.x*s,this._y=axis.y*s,this._z=axis.z*s,this._w=Math.cos(halfAngle),this.onChangeCallback(),this},setFromRotationMatrix:function(m){var s,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10],trace=m11+m22+m33;return trace>0?(s=.5/Math.sqrt(trace+1),this._w=.25/s,this._x=(m32-m23)*s,this._y=(m13-m31)*s,this._z=(m21-m12)*s):m11>m22&&m11>m33?(s=2*Math.sqrt(1+m11-m22-m33),this._w=(m32-m23)/s,this._x=.25*s,this._y=(m12+m21)/s,this._z=(m13+m31)/s):m22>m33?(s=2*Math.sqrt(1+m22-m11-m33),this._w=(m13-m31)/s,this._x=(m12+m21)/s,this._y=.25*s,this._z=(m23+m32)/s):(s=2*Math.sqrt(1+m33-m11-m22),this._w=(m21-m12)/s,this._x=(m13+m31)/s,this._y=(m23+m32)/s,this._z=.25*s),this.onChangeCallback(),this},setFromUnitVectors:function(){var v1,r,EPS=1e-6;return function(vFrom,vTo){return void 0===v1&&(v1=new THREE.Vector3),r=vFrom.dot(vTo)+1,EPS>r?(r=0,Math.abs(vFrom.x)>Math.abs(vFrom.z)?v1.set(-vFrom.y,vFrom.x,0):v1.set(0,-vFrom.z,vFrom.y)):v1.crossVectors(vFrom,vTo),this._x=v1.x,this._y=v1.y,this._z=v1.z,this._w=r,this.normalize(),this}}(),inverse:function(){return this.conjugate().normalize(),this},conjugate:function(){return this._x*=-1,this._y*=-1,this._z*=-1,this.onChangeCallback(),this},dot:function(v){return this._x*v._x+this._y*v._y+this._z*v._z+this._w*v._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var l=this.length();return 0===l?(this._x=0,this._y=0,this._z=0,this._w=1):(l=1/l,this._x=this._x*l,this._y=this._y*l,this._z=this._z*l,this._w=this._w*l),this.onChangeCallback(),this},multiply:function(q,p){return void 0!==p?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(q,p)):this.multiplyQuaternions(this,q)},multiplyQuaternions:function(a,b){var qax=a._x,qay=a._y,qaz=a._z,qaw=a._w,qbx=b._x,qby=b._y,qbz=b._z,qbw=b._w;return this._x=qax*qbw+qaw*qbx+qay*qbz-qaz*qby,this._y=qay*qbw+qaw*qby+qaz*qbx-qax*qbz,this._z=qaz*qbw+qaw*qbz+qax*qby-qay*qbx,this._w=qaw*qbw-qax*qbx-qay*qby-qaz*qbz,this.onChangeCallback(),this},multiplyVector3:function(vector){return console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."),vector.applyQuaternion(this)},slerp:function(qb,t){if(0===t)return this;if(1===t)return this.copy(qb);var x=this._x,y=this._y,z=this._z,w=this._w,cosHalfTheta=w*qb._w+x*qb._x+y*qb._y+z*qb._z;if(0>cosHalfTheta?(this._w=-qb._w,this._x=-qb._x,this._y=-qb._y,this._z=-qb._z,cosHalfTheta=-cosHalfTheta):this.copy(qb),cosHalfTheta>=1)return this._w=w,this._x=x,this._y=y,this._z=z,this;var halfTheta=Math.acos(cosHalfTheta),sinHalfTheta=Math.sqrt(1-cosHalfTheta*cosHalfTheta);
+if(Math.abs(sinHalfTheta)<.001)return this._w=.5*(w+this._w),this._x=.5*(x+this._x),this._y=.5*(y+this._y),this._z=.5*(z+this._z),this;var ratioA=Math.sin((1-t)*halfTheta)/sinHalfTheta,ratioB=Math.sin(t*halfTheta)/sinHalfTheta;return this._w=w*ratioA+this._w*ratioB,this._x=x*ratioA+this._x*ratioB,this._y=y*ratioA+this._y*ratioB,this._z=z*ratioA+this._z*ratioB,this.onChangeCallback(),this},equals:function(quaternion){return quaternion._x===this._x&&quaternion._y===this._y&&quaternion._z===this._z&&quaternion._w===this._w},fromArray:function(array,offset){return void 0===offset&&(offset=0),this._x=array[offset],this._y=array[offset+1],this._z=array[offset+2],this._w=array[offset+3],this.onChangeCallback(),this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this._x,array[offset+1]=this._y,array[offset+2]=this._z,array[offset+3]=this._w,array},onChange:function(callback){return this.onChangeCallback=callback,this},onChangeCallback:function(){}},THREE.Quaternion.slerp=function(qa,qb,qm,t){return qm.copy(qa).slerp(qb,t)},THREE.Vector2=function(x,y){this.x=x||0,this.y=y||0},THREE.Vector2.prototype={constructor:THREE.Vector2,get width(){return this.x},set width(value){this.x=value},get height(){return this.y},set height(value){this.y=value},set:function(x,y){return this.x=x,this.y=y,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+index)}},clone:function(){return new this.constructor(this.x,this.y)},copy:function(v){return this.x=v.x,this.y=v.y,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this)},addScalar:function(s){return this.x+=s,this.y+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this},addScaledVector:function(v,s){return this.x+=v.x*s,this.y+=v.y*s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this)},subScalar:function(s){return this.x-=s,this.y-=s,this},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this},multiply:function(v){return this.x*=v.x,this.y*=v.y,this},multiplyScalar:function(scalar){return isFinite(scalar)?(this.x*=scalar,this.y*=scalar):(this.x=0,this.y=0),this},divide:function(v){return this.x/=v.x,this.y/=v.y,this},divideScalar:function(scalar){return this.multiplyScalar(1/scalar)},min:function(v){return this.x=Math.min(this.x,v.x),this.y=Math.min(this.y,v.y),this},max:function(v){return this.x=Math.max(this.x,v.x),this.y=Math.max(this.y,v.y),this},clamp:function(min,max){return this.x=Math.max(min.x,Math.min(max.x,this.x)),this.y=Math.max(min.y,Math.min(max.y,this.y)),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector2,max=new THREE.Vector2),min.set(minVal,minVal),max.set(maxVal,maxVal),this.clamp(min,max)}}(),clampLength:function(min,max){var length=this.length();return this.multiplyScalar(Math.max(min,Math.min(max,length))/length),this},floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this},negate:function(){return this.x=-this.x,this.y=-this.y,this},dot:function(v){return this.x*v.x+this.y*v.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)},normalize:function(){return this.divideScalar(this.length())},distanceTo:function(v){return Math.sqrt(this.distanceToSquared(v))},distanceToSquared:function(v){var dx=this.x-v.x,dy=this.y-v.y;return dx*dx+dy*dy},setLength:function(length){return this.multiplyScalar(length/this.length())},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this},lerpVectors:function(v1,v2,alpha){return this.subVectors(v2,v1).multiplyScalar(alpha).add(v1),this},equals:function(v){return v.x===this.x&&v.y===this.y},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this},rotateAround:function(center,angle){var c=Math.cos(angle),s=Math.sin(angle),x=this.x-center.x,y=this.y-center.y;return this.x=x*c-y*s+center.x,this.y=x*s+y*c+center.y,this}},THREE.Vector3=function(x,y,z){this.x=x||0,this.y=y||0,this.z=z||0},THREE.Vector3.prototype={constructor:THREE.Vector3,set:function(x,y,z){return this.x=x,this.y=y,this.z=z,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setZ:function(z){return this.z=z,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;case 2:this.z=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+index)}},clone:function(){return new this.constructor(this.x,this.y,this.z)},copy:function(v){return this.x=v.x,this.y=v.y,this.z=v.z,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this.z+=v.z,this)},addScalar:function(s){return this.x+=s,this.y+=s,this.z+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this.z=a.z+b.z,this},addScaledVector:function(v,s){return this.x+=v.x*s,this.y+=v.y*s,this.z+=v.z*s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this.z-=v.z,this)},subScalar:function(s){return this.x-=s,this.y-=s,this.z-=s,this},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this.z=a.z-b.z,this},multiply:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(v,w)):(this.x*=v.x,this.y*=v.y,this.z*=v.z,this)},multiplyScalar:function(scalar){return isFinite(scalar)?(this.x*=scalar,this.y*=scalar,this.z*=scalar):(this.x=0,this.y=0,this.z=0),this},multiplyVectors:function(a,b){return this.x=a.x*b.x,this.y=a.y*b.y,this.z=a.z*b.z,this},applyEuler:function(){var quaternion;return function(euler){return euler instanceof THREE.Euler==!1&&console.error("THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order."),void 0===quaternion&&(quaternion=new THREE.Quaternion),this.applyQuaternion(quaternion.setFromEuler(euler)),this}}(),applyAxisAngle:function(){var quaternion;return function(axis,angle){return void 0===quaternion&&(quaternion=new THREE.Quaternion),this.applyQuaternion(quaternion.setFromAxisAngle(axis,angle)),this}}(),applyMatrix3:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[3]*y+e[6]*z,this.y=e[1]*x+e[4]*y+e[7]*z,this.z=e[2]*x+e[5]*y+e[8]*z,this},applyMatrix4:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z+e[12],this.y=e[1]*x+e[5]*y+e[9]*z+e[13],this.z=e[2]*x+e[6]*y+e[10]*z+e[14],this},applyProjection:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements,d=1/(e[3]*x+e[7]*y+e[11]*z+e[15]);return this.x=(e[0]*x+e[4]*y+e[8]*z+e[12])*d,this.y=(e[1]*x+e[5]*y+e[9]*z+e[13])*d,this.z=(e[2]*x+e[6]*y+e[10]*z+e[14])*d,this},applyQuaternion:function(q){var x=this.x,y=this.y,z=this.z,qx=q.x,qy=q.y,qz=q.z,qw=q.w,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;return this.x=ix*qw+iw*-qx+iy*-qz-iz*-qy,this.y=iy*qw+iw*-qy+iz*-qx-ix*-qz,this.z=iz*qw+iw*-qz+ix*-qy-iy*-qx,this},project:function(){var matrix;return function(camera){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.multiplyMatrices(camera.projectionMatrix,matrix.getInverse(camera.matrixWorld)),this.applyProjection(matrix)}}(),unproject:function(){var matrix;return function(camera){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.multiplyMatrices(camera.matrixWorld,matrix.getInverse(camera.projectionMatrix)),this.applyProjection(matrix)}}(),transformDirection:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z,this.y=e[1]*x+e[5]*y+e[9]*z,this.z=e[2]*x+e[6]*y+e[10]*z,this.normalize(),this},divide:function(v){return this.x/=v.x,this.y/=v.y,this.z/=v.z,this},divideScalar:function(scalar){return this.multiplyScalar(1/scalar)},min:function(v){return this.x=Math.min(this.x,v.x),this.y=Math.min(this.y,v.y),this.z=Math.min(this.z,v.z),this},max:function(v){return this.x=Math.max(this.x,v.x),this.y=Math.max(this.y,v.y),this.z=Math.max(this.z,v.z),this},clamp:function(min,max){return this.x=Math.max(min.x,Math.min(max.x,this.x)),this.y=Math.max(min.y,Math.min(max.y,this.y)),this.z=Math.max(min.z,Math.min(max.z,this.z)),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector3,max=new THREE.Vector3),min.set(minVal,minVal,minVal),max.set(maxVal,maxVal,maxVal),this.clamp(min,max)}}(),clampLength:function(min,max){var length=this.length();return this.multiplyScalar(Math.max(min,Math.min(max,length))/length),this},floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this},dot:function(v){return this.x*v.x+this.y*v.y+this.z*v.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)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length())},setLength:function(length){return this.multiplyScalar(length/this.length())},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this.z+=(v.z-this.z)*alpha,this},lerpVectors:function(v1,v2,alpha){return this.subVectors(v2,v1).multiplyScalar(alpha).add(v1),this},cross:function(v,w){if(void 0!==w)return console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(v,w);var x=this.x,y=this.y,z=this.z;return this.x=y*v.z-z*v.y,this.y=z*v.x-x*v.z,this.z=x*v.y-y*v.x,this},crossVectors:function(a,b){var ax=a.x,ay=a.y,az=a.z,bx=b.x,by=b.y,bz=b.z;return this.x=ay*bz-az*by,this.y=az*bx-ax*bz,this.z=ax*by-ay*bx,this},projectOnVector:function(){var v1,dot;return function(vector){return void 0===v1&&(v1=new THREE.Vector3),v1.copy(vector).normalize(),dot=this.dot(v1),this.copy(v1).multiplyScalar(dot)}}(),projectOnPlane:function(){var v1;return function(planeNormal){return void 0===v1&&(v1=new THREE.Vector3),v1.copy(this).projectOnVector(planeNormal),this.sub(v1)}}(),reflect:function(){var v1;return function(normal){return void 0===v1&&(v1=new THREE.Vector3),this.sub(v1.copy(normal).multiplyScalar(2*this.dot(normal)))}}(),angleTo:function(v){var theta=this.dot(v)/(this.length()*v.length());return Math.acos(THREE.Math.clamp(theta,-1,1))},distanceTo:function(v){return Math.sqrt(this.distanceToSquared(v))},distanceToSquared:function(v){var dx=this.x-v.x,dy=this.y-v.y,dz=this.z-v.z;return dx*dx+dy*dy+dz*dz},setEulerFromRotationMatrix:function(m,order){console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.")},setEulerFromQuaternion:function(q,order){console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.")},getPositionFromMatrix:function(m){return console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."),this.setFromMatrixPosition(m)},getScaleFromMatrix:function(m){return console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."),this.setFromMatrixScale(m)},getColumnFromMatrix:function(index,matrix){return console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."),this.setFromMatrixColumn(index,matrix)},setFromMatrixPosition:function(m){return this.x=m.elements[12],this.y=m.elements[13],this.z=m.elements[14],this},setFromMatrixScale:function(m){var sx=this.set(m.elements[0],m.elements[1],m.elements[2]).length(),sy=this.set(m.elements[4],m.elements[5],m.elements[6]).length(),sz=this.set(m.elements[8],m.elements[9],m.elements[10]).length();return this.x=sx,this.y=sy,this.z=sz,this},setFromMatrixColumn:function(index,matrix){var offset=4*index,me=matrix.elements;return this.x=me[offset],this.y=me[offset+1],this.z=me[offset+2],this},equals:function(v){return v.x===this.x&&v.y===this.y&&v.z===this.z},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this.z=array[offset+2],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array[offset+2]=this.z,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this.z=attribute.array[index+2],this}},THREE.Vector4=function(x,y,z,w){this.x=x||0,this.y=y||0,this.z=z||0,this.w=void 0!==w?w:1},THREE.Vector4.prototype={constructor:THREE.Vector4,set:function(x,y,z,w){return this.x=x,this.y=y,this.z=z,this.w=w,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setZ:function(z){return this.z=z,this},setW:function(w){return this.w=w,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;case 2:this.z=value;break;case 3:this.w=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("index is out of range: "+index)}},clone:function(){return new this.constructor(this.x,this.y,this.z,this.w)},copy:function(v){return this.x=v.x,this.y=v.y,this.z=v.z,this.w=void 0!==v.w?v.w:1,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this.z+=v.z,this.w+=v.w,this)},addScalar:function(s){return this.x+=s,this.y+=s,this.z+=s,this.w+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this.z=a.z+b.z,this.w=a.w+b.w,this},addScaledVector:function(v,s){return this.x+=v.x*s,this.y+=v.y*s,this.z+=v.z*s,this.w+=v.w*s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this.z-=v.z,this.w-=v.w,this)},subScalar:function(s){return this.x-=s,this.y-=s,this.z-=s,this.w-=s,this},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this.z=a.z-b.z,this.w=a.w-b.w,this},multiplyScalar:function(scalar){return isFinite(scalar)?(this.x*=scalar,this.y*=scalar,this.z*=scalar,this.w*=scalar):(this.x=0,this.y=0,this.z=0,this.w=0),this},applyMatrix4:function(m){var x=this.x,y=this.y,z=this.z,w=this.w,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z+e[12]*w,this.y=e[1]*x+e[5]*y+e[9]*z+e[13]*w,this.z=e[2]*x+e[6]*y+e[10]*z+e[14]*w,this.w=e[3]*x+e[7]*y+e[11]*z+e[15]*w,this},divideScalar:function(scalar){return this.multiplyScalar(1/scalar)},setAxisAngleFromQuaternion:function(q){this.w=2*Math.acos(q.w);var s=Math.sqrt(1-q.w*q.w);return 1e-4>s?(this.x=1,this.y=0,this.z=0):(this.x=q.x/s,this.y=q.y/s,this.z=q.z/s),this},setAxisAngleFromRotationMatrix:function(m){var angle,x,y,z,epsilon=.01,epsilon2=.1,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10];if(Math.abs(m12-m21)yy&&xx>zz?epsilon>xx?(x=0,y=.707106781,z=.707106781):(x=Math.sqrt(xx),y=xy/x,z=xz/x):yy>zz?epsilon>yy?(x=.707106781,y=0,z=.707106781):(y=Math.sqrt(yy),x=xy/y,z=yz/y):epsilon>zz?(x=.707106781,y=.707106781,z=0):(z=Math.sqrt(zz),x=xz/z,y=yz/z),this.set(x,y,z,angle),this}var s=Math.sqrt((m32-m23)*(m32-m23)+(m13-m31)*(m13-m31)+(m21-m12)*(m21-m12));return Math.abs(s)<.001&&(s=1),this.x=(m32-m23)/s,this.y=(m13-m31)/s,this.z=(m21-m12)/s,this.w=Math.acos((m11+m22+m33-1)/2),this},min:function(v){return this.x=Math.min(this.x,v.x),this.y=Math.min(this.y,v.y),this.z=Math.min(this.z,v.z),this.w=Math.min(this.w,v.w),this},max:function(v){return this.x=Math.max(this.x,v.x),this.y=Math.max(this.y,v.y),this.z=Math.max(this.z,v.z),this.w=Math.max(this.w,v.w),this},clamp:function(min,max){return this.x=Math.max(min.x,Math.min(max.x,this.x)),this.y=Math.max(min.y,Math.min(max.y,this.y)),this.z=Math.max(min.z,Math.min(max.z,this.z)),this.w=Math.max(min.w,Math.min(max.w,this.w)),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector4,max=new THREE.Vector4),min.set(minVal,minVal,minVal,minVal),max.set(maxVal,maxVal,maxVal,maxVal),this.clamp(min,max)}}(),floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this.w=Math.floor(this.w),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this.w=Math.ceil(this.w),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this.w=Math.round(this.w),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this.w=this.w<0?Math.ceil(this.w):Math.floor(this.w),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this.w=-this.w,this},dot:function(v){return this.x*v.x+this.y*v.y+this.z*v.z+this.w*v.w},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},lengthManhattan: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())},setLength:function(length){return this.multiplyScalar(length/this.length())},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this.z+=(v.z-this.z)*alpha,this.w+=(v.w-this.w)*alpha,this},lerpVectors:function(v1,v2,alpha){return this.subVectors(v2,v1).multiplyScalar(alpha).add(v1),this},equals:function(v){return v.x===this.x&&v.y===this.y&&v.z===this.z&&v.w===this.w},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this.z=array[offset+2],this.w=array[offset+3],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array[offset+2]=this.z,array[offset+3]=this.w,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this.z=attribute.array[index+2],this.w=attribute.array[index+3],this}},THREE.Euler=function(x,y,z,order){this._x=x||0,this._y=y||0,this._z=z||0,this._order=order||THREE.Euler.DefaultOrder},THREE.Euler.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"],THREE.Euler.DefaultOrder="XYZ",THREE.Euler.prototype={constructor:THREE.Euler,get x(){return this._x},set x(value){this._x=value,this.onChangeCallback()},get y(){return this._y},set y(value){this._y=value,this.onChangeCallback()},get z(){return this._z},set z(value){this._z=value,this.onChangeCallback()},get order(){return this._order},set order(value){this._order=value,this.onChangeCallback()},set:function(x,y,z,order){return this._x=x,this._y=y,this._z=z,this._order=order||this._order,this.onChangeCallback(),this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._order)},copy:function(euler){return this._x=euler._x,this._y=euler._y,this._z=euler._z,this._order=euler._order,this.onChangeCallback(),this},setFromRotationMatrix:function(m,order,update){var clamp=THREE.Math.clamp,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10];return order=order||this._order,"XYZ"===order?(this._y=Math.asin(clamp(m13,-1,1)),Math.abs(m13)<.99999?(this._x=Math.atan2(-m23,m33),this._z=Math.atan2(-m12,m11)):(this._x=Math.atan2(m32,m22),this._z=0)):"YXZ"===order?(this._x=Math.asin(-clamp(m23,-1,1)),Math.abs(m23)<.99999?(this._y=Math.atan2(m13,m33),this._z=Math.atan2(m21,m22)):(this._y=Math.atan2(-m31,m11),this._z=0)):"ZXY"===order?(this._x=Math.asin(clamp(m32,-1,1)),Math.abs(m32)<.99999?(this._y=Math.atan2(-m31,m33),this._z=Math.atan2(-m12,m22)):(this._y=0,this._z=Math.atan2(m21,m11))):"ZYX"===order?(this._y=Math.asin(-clamp(m31,-1,1)),Math.abs(m31)<.99999?(this._x=Math.atan2(m32,m33),this._z=Math.atan2(m21,m11)):(this._x=0,this._z=Math.atan2(-m12,m22))):"YZX"===order?(this._z=Math.asin(clamp(m21,-1,1)),Math.abs(m21)<.99999?(this._x=Math.atan2(-m23,m22),this._y=Math.atan2(-m31,m11)):(this._x=0,this._y=Math.atan2(m13,m33))):"XZY"===order?(this._z=Math.asin(-clamp(m12,-1,1)),Math.abs(m12)<.99999?(this._x=Math.atan2(m32,m22),this._y=Math.atan2(m13,m11)):(this._x=Math.atan2(-m23,m33),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+order),this._order=order,update!==!1&&this.onChangeCallback(),this},setFromQuaternion:function(){var matrix;return function(q,order,update){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.makeRotationFromQuaternion(q),this.setFromRotationMatrix(matrix,order,update),this}}(),setFromVector3:function(v,order){return this.set(v.x,v.y,v.z,order||this._order)},reorder:function(){var q=new THREE.Quaternion;return function(newOrder){q.setFromEuler(this),this.setFromQuaternion(q,newOrder)}}(),equals:function(euler){return euler._x===this._x&&euler._y===this._y&&euler._z===this._z&&euler._order===this._order},fromArray:function(array){return this._x=array[0],this._y=array[1],this._z=array[2],void 0!==array[3]&&(this._order=array[3]),this.onChangeCallback(),this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this._x,array[offset+1]=this._y,array[offset+2]=this._z,array[offset+3]=this._order,array},toVector3:function(optionalResult){return optionalResult?optionalResult.set(this._x,this._y,this._z):new THREE.Vector3(this._x,this._y,this._z)},onChange:function(callback){return this.onChangeCallback=callback,this},onChangeCallback:function(){}},THREE.Line3=function(start,end){this.start=void 0!==start?start:new THREE.Vector3,this.end=void 0!==end?end:new THREE.Vector3},THREE.Line3.prototype={constructor:THREE.Line3,set:function(start,end){return this.start.copy(start),this.end.copy(end),this},clone:function(){return(new this.constructor).copy(this)},copy:function(line){return this.start.copy(line.start),this.end.copy(line.end),this},center:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.addVectors(this.start,this.end).multiplyScalar(.5)},delta:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(t,optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.delta(result).multiplyScalar(t).add(this.start)},closestPointToPointParameter:function(){var startP=new THREE.Vector3,startEnd=new THREE.Vector3;return function(point,clampToLine){startP.subVectors(point,this.start),startEnd.subVectors(this.end,this.start);var startEnd2=startEnd.dot(startEnd),startEnd_startP=startEnd.dot(startP),t=startEnd_startP/startEnd2;return clampToLine&&(t=THREE.Math.clamp(t,0,1)),t}}(),closestPointToPoint:function(point,clampToLine,optionalTarget){var t=this.closestPointToPointParameter(point,clampToLine),result=optionalTarget||new THREE.Vector3;return this.delta(result).multiplyScalar(t).add(this.start)},applyMatrix4:function(matrix){return this.start.applyMatrix4(matrix),this.end.applyMatrix4(matrix),this},equals:function(line){return line.start.equals(this.start)&&line.end.equals(this.end)}},THREE.Box2=function(min,max){this.min=void 0!==min?min:new THREE.Vector2(1/0,1/0),this.max=void 0!==max?max:new THREE.Vector2(-(1/0),-(1/0))},THREE.Box2.prototype={constructor:THREE.Box2,set:function(min,max){return this.min.copy(min),this.max.copy(max),this},setFromPoints:function(points){this.makeEmpty();for(var i=0,il=points.length;il>i;i++)this.expandByPoint(points[i]);return this},setFromCenterAndSize:function(){var v1=new THREE.Vector2;return function(center,size){var halfSize=v1.copy(size).multiplyScalar(.5);return this.min.copy(center).sub(halfSize),this.max.copy(center).add(halfSize),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(box){return this.min.copy(box.min),this.max.copy(box.max),this},makeEmpty:function(){return this.min.x=this.min.y=1/0,this.max.x=this.max.y=-(1/0),this},empty:function(){return this.max.xthis.max.x||point.ythis.max.y)},containsBox:function(box){return this.min.x<=box.min.x&&box.max.x<=this.max.x&&this.min.y<=box.min.y&&box.max.y<=this.max.y},getParameter:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector2;return result.set((point.x-this.min.x)/(this.max.x-this.min.x),(point.y-this.min.y)/(this.max.y-this.min.y))},isIntersectionBox:function(box){return!(box.max.xthis.max.x||box.max.ythis.max.y)},clampPoint:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector2;return result.copy(point).clamp(this.min,this.max)},distanceToPoint:function(){var v1=new THREE.Vector2;return function(point){var clampedPoint=v1.copy(point).clamp(this.min,this.max);return clampedPoint.sub(point).length()}}(),intersect:function(box){return this.min.max(box.min),this.max.min(box.max),this},union:function(box){return this.min.min(box.min),this.max.max(box.max),this},translate:function(offset){return this.min.add(offset),this.max.add(offset),this},equals:function(box){return box.min.equals(this.min)&&box.max.equals(this.max)}},THREE.Box3=function(min,max){this.min=void 0!==min?min:new THREE.Vector3(1/0,1/0,1/0),this.max=void 0!==max?max:new THREE.Vector3(-(1/0),-(1/0),-(1/0))},THREE.Box3.prototype={constructor:THREE.Box3,set:function(min,max){return this.min.copy(min),this.max.copy(max),this},setFromPoints:function(points){this.makeEmpty();for(var i=0,il=points.length;il>i;i++)this.expandByPoint(points[i]);return this},setFromCenterAndSize:function(){var v1=new THREE.Vector3;return function(center,size){var halfSize=v1.copy(size).multiplyScalar(.5);return this.min.copy(center).sub(halfSize),this.max.copy(center).add(halfSize),this}}(),setFromObject:function(){var v1=new THREE.Vector3;return function(object){var scope=this;return object.updateMatrixWorld(!0),this.makeEmpty(),object.traverse(function(node){var geometry=node.geometry;if(void 0!==geometry)if(geometry instanceof THREE.Geometry)for(var vertices=geometry.vertices,i=0,il=vertices.length;il>i;i++)v1.copy(vertices[i]),v1.applyMatrix4(node.matrixWorld),scope.expandByPoint(v1);else if(geometry instanceof THREE.BufferGeometry&&void 0!==geometry.attributes.position)for(var positions=geometry.attributes.position.array,i=0,il=positions.length;il>i;i+=3)v1.set(positions[i],positions[i+1],positions[i+2]),v1.applyMatrix4(node.matrixWorld),scope.expandByPoint(v1)}),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(box){return this.min.copy(box.min),this.max.copy(box.max),this},makeEmpty:function(){return this.min.x=this.min.y=this.min.z=1/0,this.max.x=this.max.y=this.max.z=-(1/0),this},empty:function(){return this.max.xthis.max.x||point.ythis.max.y||point.zthis.max.z)},containsBox:function(box){return this.min.x<=box.min.x&&box.max.x<=this.max.x&&this.min.y<=box.min.y&&box.max.y<=this.max.y&&this.min.z<=box.min.z&&box.max.z<=this.max.z},getParameter:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.set((point.x-this.min.x)/(this.max.x-this.min.x),(point.y-this.min.y)/(this.max.y-this.min.y),(point.z-this.min.z)/(this.max.z-this.min.z))},isIntersectionBox:function(box){return!(box.max.xthis.max.x||box.max.ythis.max.y||box.max.zthis.max.z)},clampPoint:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.copy(point).clamp(this.min,this.max)},distanceToPoint:function(){var v1=new THREE.Vector3;return function(point){
+var clampedPoint=v1.copy(point).clamp(this.min,this.max);return clampedPoint.sub(point).length()}}(),getBoundingSphere:function(){var v1=new THREE.Vector3;return function(optionalTarget){var result=optionalTarget||new THREE.Sphere;return result.center=this.center(),result.radius=.5*this.size(v1).length(),result}}(),intersect:function(box){return this.min.max(box.min),this.max.min(box.max),this},union:function(box){return this.min.min(box.min),this.max.max(box.max),this},applyMatrix4:function(){var points=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];return function(matrix){return points[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(matrix),points[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(matrix),points[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(matrix),points[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(matrix),points[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(matrix),points[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(matrix),points[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(matrix),points[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(matrix),this.makeEmpty(),this.setFromPoints(points),this}}(),translate:function(offset){return this.min.add(offset),this.max.add(offset),this},equals:function(box){return box.min.equals(this.min)&&box.max.equals(this.max)}},THREE.Matrix3=function(){this.elements=new Float32Array([1,0,0,0,1,0,0,0,1]),arguments.length>0&&console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.")},THREE.Matrix3.prototype={constructor:THREE.Matrix3,set:function(n11,n12,n13,n21,n22,n23,n31,n32,n33){var te=this.elements;return te[0]=n11,te[3]=n12,te[6]=n13,te[1]=n21,te[4]=n22,te[7]=n23,te[2]=n31,te[5]=n32,te[8]=n33,this},identity:function(){return this.set(1,0,0,0,1,0,0,0,1),this},clone:function(){return(new this.constructor).fromArray(this.elements)},copy:function(m){var me=m.elements;return this.set(me[0],me[3],me[6],me[1],me[4],me[7],me[2],me[5],me[8]),this},multiplyVector3:function(vector){return console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead."),vector.applyMatrix3(this)},multiplyVector3Array:function(a){return console.warn("THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead."),this.applyToVector3Array(a)},applyToVector3Array:function(){var v1;return function(array,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=array.length);for(var i=0,j=offset;length>i;i+=3,j+=3)v1.fromArray(array,j),v1.applyMatrix3(this),v1.toArray(array,j);return array}}(),applyToBuffer:function(){var v1;return function(buffer,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=buffer.length/buffer.itemSize);for(var i=0,j=offset;length>i;i++,j++)v1.x=buffer.getX(j),v1.y=buffer.getY(j),v1.z=buffer.getZ(j),v1.applyMatrix3(this),buffer.setXYZ(v1.x,v1.y,v1.z);return buffer}}(),multiplyScalar:function(s){var te=this.elements;return te[0]*=s,te[3]*=s,te[6]*=s,te[1]*=s,te[4]*=s,te[7]*=s,te[2]*=s,te[5]*=s,te[8]*=s,this},determinant:function(){var te=this.elements,a=te[0],b=te[1],c=te[2],d=te[3],e=te[4],f=te[5],g=te[6],h=te[7],i=te[8];return a*e*i-a*f*h-b*d*i+b*f*g+c*d*h-c*e*g},getInverse:function(matrix,throwOnInvertible){var me=matrix.elements,te=this.elements;te[0]=me[10]*me[5]-me[6]*me[9],te[1]=-me[10]*me[1]+me[2]*me[9],te[2]=me[6]*me[1]-me[2]*me[5],te[3]=-me[10]*me[4]+me[6]*me[8],te[4]=me[10]*me[0]-me[2]*me[8],te[5]=-me[6]*me[0]+me[2]*me[4],te[6]=me[9]*me[4]-me[5]*me[8],te[7]=-me[9]*me[0]+me[1]*me[8],te[8]=me[5]*me[0]-me[1]*me[4];var det=me[0]*te[0]+me[1]*te[3]+me[2]*te[6];if(0===det){var msg="Matrix3.getInverse(): can't invert matrix, determinant is 0";if(throwOnInvertible)throw new Error(msg);return console.warn(msg),this.identity(),this}return this.multiplyScalar(1/det),this},transpose:function(){var tmp,m=this.elements;return tmp=m[1],m[1]=m[3],m[3]=tmp,tmp=m[2],m[2]=m[6],m[6]=tmp,tmp=m[5],m[5]=m[7],m[7]=tmp,this},flattenToArrayOffset:function(array,offset){var te=this.elements;return array[offset]=te[0],array[offset+1]=te[1],array[offset+2]=te[2],array[offset+3]=te[3],array[offset+4]=te[4],array[offset+5]=te[5],array[offset+6]=te[6],array[offset+7]=te[7],array[offset+8]=te[8],array},getNormalMatrix:function(m){return this.getInverse(m).transpose(),this},transposeIntoArray:function(r){var m=this.elements;return r[0]=m[0],r[1]=m[3],r[2]=m[6],r[3]=m[1],r[4]=m[4],r[5]=m[7],r[6]=m[2],r[7]=m[5],r[8]=m[8],this},fromArray:function(array){return this.elements.set(array),this},toArray:function(){var te=this.elements;return[te[0],te[1],te[2],te[3],te[4],te[5],te[6],te[7],te[8]]}},THREE.Matrix4=function(){this.elements=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),arguments.length>0&&console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.")},THREE.Matrix4.prototype={constructor:THREE.Matrix4,set:function(n11,n12,n13,n14,n21,n22,n23,n24,n31,n32,n33,n34,n41,n42,n43,n44){var te=this.elements;return te[0]=n11,te[4]=n12,te[8]=n13,te[12]=n14,te[1]=n21,te[5]=n22,te[9]=n23,te[13]=n24,te[2]=n31,te[6]=n32,te[10]=n33,te[14]=n34,te[3]=n41,te[7]=n42,te[11]=n43,te[15]=n44,this},identity:function(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this},clone:function(){return(new THREE.Matrix4).fromArray(this.elements)},copy:function(m){return this.elements.set(m.elements),this},extractPosition:function(m){return console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()."),this.copyPosition(m)},copyPosition:function(m){var te=this.elements,me=m.elements;return te[12]=me[12],te[13]=me[13],te[14]=me[14],this},extractBasis:function(xAxis,yAxis,zAxis){var te=this.elements;return xAxis.set(te[0],te[1],te[2]),yAxis.set(te[4],te[5],te[6]),zAxis.set(te[8],te[9],te[10]),this},makeBasis:function(xAxis,yAxis,zAxis){return this.set(xAxis.x,yAxis.x,zAxis.x,0,xAxis.y,yAxis.y,zAxis.y,0,xAxis.z,yAxis.z,zAxis.z,0,0,0,0,1),this},extractRotation:function(){var v1;return function(m){void 0===v1&&(v1=new THREE.Vector3);var te=this.elements,me=m.elements,scaleX=1/v1.set(me[0],me[1],me[2]).length(),scaleY=1/v1.set(me[4],me[5],me[6]).length(),scaleZ=1/v1.set(me[8],me[9],me[10]).length();return te[0]=me[0]*scaleX,te[1]=me[1]*scaleX,te[2]=me[2]*scaleX,te[4]=me[4]*scaleY,te[5]=me[5]*scaleY,te[6]=me[6]*scaleY,te[8]=me[8]*scaleZ,te[9]=me[9]*scaleZ,te[10]=me[10]*scaleZ,this}}(),makeRotationFromEuler:function(euler){euler instanceof THREE.Euler==!1&&console.error("THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var te=this.elements,x=euler.x,y=euler.y,z=euler.z,a=Math.cos(x),b=Math.sin(x),c=Math.cos(y),d=Math.sin(y),e=Math.cos(z),f=Math.sin(z);if("XYZ"===euler.order){var ae=a*e,af=a*f,be=b*e,bf=b*f;te[0]=c*e,te[4]=-c*f,te[8]=d,te[1]=af+be*d,te[5]=ae-bf*d,te[9]=-b*c,te[2]=bf-ae*d,te[6]=be+af*d,te[10]=a*c}else if("YXZ"===euler.order){var ce=c*e,cf=c*f,de=d*e,df=d*f;te[0]=ce+df*b,te[4]=de*b-cf,te[8]=a*d,te[1]=a*f,te[5]=a*e,te[9]=-b,te[2]=cf*b-de,te[6]=df+ce*b,te[10]=a*c}else if("ZXY"===euler.order){var ce=c*e,cf=c*f,de=d*e,df=d*f;te[0]=ce-df*b,te[4]=-a*f,te[8]=de+cf*b,te[1]=cf+de*b,te[5]=a*e,te[9]=df-ce*b,te[2]=-a*d,te[6]=b,te[10]=a*c}else if("ZYX"===euler.order){var ae=a*e,af=a*f,be=b*e,bf=b*f;te[0]=c*e,te[4]=be*d-af,te[8]=ae*d+bf,te[1]=c*f,te[5]=bf*d+ae,te[9]=af*d-be,te[2]=-d,te[6]=b*c,te[10]=a*c}else if("YZX"===euler.order){var ac=a*c,ad=a*d,bc=b*c,bd=b*d;te[0]=c*e,te[4]=bd-ac*f,te[8]=bc*f+ad,te[1]=f,te[5]=a*e,te[9]=-b*e,te[2]=-d*e,te[6]=ad*f+bc,te[10]=ac-bd*f}else if("XZY"===euler.order){var ac=a*c,ad=a*d,bc=b*c,bd=b*d;te[0]=c*e,te[4]=-f,te[8]=d*e,te[1]=ac*f+bd,te[5]=a*e,te[9]=ad*f-bc,te[2]=bc*f-ad,te[6]=b*e,te[10]=bd*f+ac}return te[3]=0,te[7]=0,te[11]=0,te[12]=0,te[13]=0,te[14]=0,te[15]=1,this},setRotationFromQuaternion:function(q){return console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()."),this.makeRotationFromQuaternion(q)},makeRotationFromQuaternion:function(q){var te=this.elements,x=q.x,y=q.y,z=q.z,w=q.w,x2=x+x,y2=y+y,z2=z+z,xx=x*x2,xy=x*y2,xz=x*z2,yy=y*y2,yz=y*z2,zz=z*z2,wx=w*x2,wy=w*y2,wz=w*z2;return te[0]=1-(yy+zz),te[4]=xy-wz,te[8]=xz+wy,te[1]=xy+wz,te[5]=1-(xx+zz),te[9]=yz-wx,te[2]=xz-wy,te[6]=yz+wx,te[10]=1-(xx+yy),te[3]=0,te[7]=0,te[11]=0,te[12]=0,te[13]=0,te[14]=0,te[15]=1,this},lookAt:function(){var x,y,z;return function(eye,target,up){void 0===x&&(x=new THREE.Vector3),void 0===y&&(y=new THREE.Vector3),void 0===z&&(z=new THREE.Vector3);var te=this.elements;return z.subVectors(eye,target).normalize(),0===z.lengthSq()&&(z.z=1),x.crossVectors(up,z).normalize(),0===x.lengthSq()&&(z.x+=1e-4,x.crossVectors(up,z).normalize()),y.crossVectors(z,x),te[0]=x.x,te[4]=y.x,te[8]=z.x,te[1]=x.y,te[5]=y.y,te[9]=z.y,te[2]=x.z,te[6]=y.z,te[10]=z.z,this}}(),multiply:function(m,n){return void 0!==n?(console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(m,n)):this.multiplyMatrices(this,m)},multiplyMatrices:function(a,b){var ae=a.elements,be=b.elements,te=this.elements,a11=ae[0],a12=ae[4],a13=ae[8],a14=ae[12],a21=ae[1],a22=ae[5],a23=ae[9],a24=ae[13],a31=ae[2],a32=ae[6],a33=ae[10],a34=ae[14],a41=ae[3],a42=ae[7],a43=ae[11],a44=ae[15],b11=be[0],b12=be[4],b13=be[8],b14=be[12],b21=be[1],b22=be[5],b23=be[9],b24=be[13],b31=be[2],b32=be[6],b33=be[10],b34=be[14],b41=be[3],b42=be[7],b43=be[11],b44=be[15];return te[0]=a11*b11+a12*b21+a13*b31+a14*b41,te[4]=a11*b12+a12*b22+a13*b32+a14*b42,te[8]=a11*b13+a12*b23+a13*b33+a14*b43,te[12]=a11*b14+a12*b24+a13*b34+a14*b44,te[1]=a21*b11+a22*b21+a23*b31+a24*b41,te[5]=a21*b12+a22*b22+a23*b32+a24*b42,te[9]=a21*b13+a22*b23+a23*b33+a24*b43,te[13]=a21*b14+a22*b24+a23*b34+a24*b44,te[2]=a31*b11+a32*b21+a33*b31+a34*b41,te[6]=a31*b12+a32*b22+a33*b32+a34*b42,te[10]=a31*b13+a32*b23+a33*b33+a34*b43,te[14]=a31*b14+a32*b24+a33*b34+a34*b44,te[3]=a41*b11+a42*b21+a43*b31+a44*b41,te[7]=a41*b12+a42*b22+a43*b32+a44*b42,te[11]=a41*b13+a42*b23+a43*b33+a44*b43,te[15]=a41*b14+a42*b24+a43*b34+a44*b44,this},multiplyToArray:function(a,b,r){var te=this.elements;return this.multiplyMatrices(a,b),r[0]=te[0],r[1]=te[1],r[2]=te[2],r[3]=te[3],r[4]=te[4],r[5]=te[5],r[6]=te[6],r[7]=te[7],r[8]=te[8],r[9]=te[9],r[10]=te[10],r[11]=te[11],r[12]=te[12],r[13]=te[13],r[14]=te[14],r[15]=te[15],this},multiplyScalar:function(s){var te=this.elements;return te[0]*=s,te[4]*=s,te[8]*=s,te[12]*=s,te[1]*=s,te[5]*=s,te[9]*=s,te[13]*=s,te[2]*=s,te[6]*=s,te[10]*=s,te[14]*=s,te[3]*=s,te[7]*=s,te[11]*=s,te[15]*=s,this},multiplyVector3:function(vector){return console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead."),vector.applyProjection(this)},multiplyVector4:function(vector){return console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead."),vector.applyMatrix4(this)},multiplyVector3Array:function(a){return console.warn("THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead."),this.applyToVector3Array(a)},applyToVector3Array:function(){var v1;return function(array,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=array.length);for(var i=0,j=offset;length>i;i+=3,j+=3)v1.fromArray(array,j),v1.applyMatrix4(this),v1.toArray(array,j);return array}}(),applyToBuffer:function(){var v1;return function(buffer,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=buffer.length/buffer.itemSize);for(var i=0,j=offset;length>i;i++,j++)v1.x=buffer.getX(j),v1.y=buffer.getY(j),v1.z=buffer.getZ(j),v1.applyMatrix4(this),buffer.setXYZ(v1.x,v1.y,v1.z);return buffer}}(),rotateAxis:function(v){console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead."),v.transformDirection(this)},crossVector:function(vector){return console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead."),vector.applyMatrix4(this)},determinant:function(){var te=this.elements,n11=te[0],n12=te[4],n13=te[8],n14=te[12],n21=te[1],n22=te[5],n23=te[9],n24=te[13],n31=te[2],n32=te[6],n33=te[10],n34=te[14],n41=te[3],n42=te[7],n43=te[11],n44=te[15];return n41*(+n14*n23*n32-n13*n24*n32-n14*n22*n33+n12*n24*n33+n13*n22*n34-n12*n23*n34)+n42*(+n11*n23*n34-n11*n24*n33+n14*n21*n33-n13*n21*n34+n13*n24*n31-n14*n23*n31)+n43*(+n11*n24*n32-n11*n22*n34-n14*n21*n32+n12*n21*n34+n14*n22*n31-n12*n24*n31)+n44*(-n13*n22*n31-n11*n23*n32+n11*n22*n33+n13*n21*n32-n12*n21*n33+n12*n23*n31)},transpose:function(){var tmp,te=this.elements;return tmp=te[1],te[1]=te[4],te[4]=tmp,tmp=te[2],te[2]=te[8],te[8]=tmp,tmp=te[6],te[6]=te[9],te[9]=tmp,tmp=te[3],te[3]=te[12],te[12]=tmp,tmp=te[7],te[7]=te[13],te[13]=tmp,tmp=te[11],te[11]=te[14],te[14]=tmp,this},flattenToArrayOffset:function(array,offset){var te=this.elements;return array[offset]=te[0],array[offset+1]=te[1],array[offset+2]=te[2],array[offset+3]=te[3],array[offset+4]=te[4],array[offset+5]=te[5],array[offset+6]=te[6],array[offset+7]=te[7],array[offset+8]=te[8],array[offset+9]=te[9],array[offset+10]=te[10],array[offset+11]=te[11],array[offset+12]=te[12],array[offset+13]=te[13],array[offset+14]=te[14],array[offset+15]=te[15],array},getPosition:function(){var v1;return function(){void 0===v1&&(v1=new THREE.Vector3),console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.");var te=this.elements;return v1.set(te[12],te[13],te[14])}}(),setPosition:function(v){var te=this.elements;return te[12]=v.x,te[13]=v.y,te[14]=v.z,this},getInverse:function(m,throwOnInvertible){var te=this.elements,me=m.elements,n11=me[0],n12=me[4],n13=me[8],n14=me[12],n21=me[1],n22=me[5],n23=me[9],n24=me[13],n31=me[2],n32=me[6],n33=me[10],n34=me[14],n41=me[3],n42=me[7],n43=me[11],n44=me[15];te[0]=n23*n34*n42-n24*n33*n42+n24*n32*n43-n22*n34*n43-n23*n32*n44+n22*n33*n44,te[4]=n14*n33*n42-n13*n34*n42-n14*n32*n43+n12*n34*n43+n13*n32*n44-n12*n33*n44,te[8]=n13*n24*n42-n14*n23*n42+n14*n22*n43-n12*n24*n43-n13*n22*n44+n12*n23*n44,te[12]=n14*n23*n32-n13*n24*n32-n14*n22*n33+n12*n24*n33+n13*n22*n34-n12*n23*n34,te[1]=n24*n33*n41-n23*n34*n41-n24*n31*n43+n21*n34*n43+n23*n31*n44-n21*n33*n44,te[5]=n13*n34*n41-n14*n33*n41+n14*n31*n43-n11*n34*n43-n13*n31*n44+n11*n33*n44,te[9]=n14*n23*n41-n13*n24*n41-n14*n21*n43+n11*n24*n43+n13*n21*n44-n11*n23*n44,te[13]=n13*n24*n31-n14*n23*n31+n14*n21*n33-n11*n24*n33-n13*n21*n34+n11*n23*n34,te[2]=n22*n34*n41-n24*n32*n41+n24*n31*n42-n21*n34*n42-n22*n31*n44+n21*n32*n44,te[6]=n14*n32*n41-n12*n34*n41-n14*n31*n42+n11*n34*n42+n12*n31*n44-n11*n32*n44,te[10]=n12*n24*n41-n14*n22*n41+n14*n21*n42-n11*n24*n42-n12*n21*n44+n11*n22*n44,te[14]=n14*n22*n31-n12*n24*n31-n14*n21*n32+n11*n24*n32+n12*n21*n34-n11*n22*n34,te[3]=n23*n32*n41-n22*n33*n41-n23*n31*n42+n21*n33*n42+n22*n31*n43-n21*n32*n43,te[7]=n12*n33*n41-n13*n32*n41+n13*n31*n42-n11*n33*n42-n12*n31*n43+n11*n32*n43,te[11]=n13*n22*n41-n12*n23*n41-n13*n21*n42+n11*n23*n42+n12*n21*n43-n11*n22*n43,te[15]=n12*n23*n31-n13*n22*n31+n13*n21*n32-n11*n23*n32-n12*n21*n33+n11*n22*n33;var det=n11*te[0]+n21*te[4]+n31*te[8]+n41*te[12];if(0===det){var msg="THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0";if(throwOnInvertible)throw new Error(msg);return console.warn(msg),this.identity(),this}return this.multiplyScalar(1/det),this},translate:function(v){console.error("THREE.Matrix4: .translate() has been removed.")},rotateX:function(angle){console.error("THREE.Matrix4: .rotateX() has been removed.")},rotateY:function(angle){console.error("THREE.Matrix4: .rotateY() has been removed.")},rotateZ:function(angle){console.error("THREE.Matrix4: .rotateZ() has been removed.")},rotateByAxis:function(axis,angle){console.error("THREE.Matrix4: .rotateByAxis() has been removed.")},scale:function(v){var te=this.elements,x=v.x,y=v.y,z=v.z;return te[0]*=x,te[4]*=y,te[8]*=z,te[1]*=x,te[5]*=y,te[9]*=z,te[2]*=x,te[6]*=y,te[10]*=z,te[3]*=x,te[7]*=y,te[11]*=z,this},getMaxScaleOnAxis:function(){var te=this.elements,scaleXSq=te[0]*te[0]+te[1]*te[1]+te[2]*te[2],scaleYSq=te[4]*te[4]+te[5]*te[5]+te[6]*te[6],scaleZSq=te[8]*te[8]+te[9]*te[9]+te[10]*te[10];return Math.sqrt(Math.max(scaleXSq,scaleYSq,scaleZSq))},makeTranslation:function(x,y,z){return this.set(1,0,0,x,0,1,0,y,0,0,1,z,0,0,0,1),this},makeRotationX:function(theta){var c=Math.cos(theta),s=Math.sin(theta);return this.set(1,0,0,0,0,c,-s,0,0,s,c,0,0,0,0,1),this},makeRotationY:function(theta){var c=Math.cos(theta),s=Math.sin(theta);return this.set(c,0,s,0,0,1,0,0,-s,0,c,0,0,0,0,1),this},makeRotationZ:function(theta){var c=Math.cos(theta),s=Math.sin(theta);return this.set(c,-s,0,0,s,c,0,0,0,0,1,0,0,0,0,1),this},makeRotationAxis:function(axis,angle){var c=Math.cos(angle),s=Math.sin(angle),t=1-c,x=axis.x,y=axis.y,z=axis.z,tx=t*x,ty=t*y;return this.set(tx*x+c,tx*y-s*z,tx*z+s*y,0,tx*y+s*z,ty*y+c,ty*z-s*x,0,tx*z-s*y,ty*z+s*x,t*z*z+c,0,0,0,0,1),this},makeScale:function(x,y,z){return this.set(x,0,0,0,0,y,0,0,0,0,z,0,0,0,0,1),this},compose:function(position,quaternion,scale){return this.makeRotationFromQuaternion(quaternion),this.scale(scale),this.setPosition(position),this},decompose:function(){var vector,matrix;return function(position,quaternion,scale){void 0===vector&&(vector=new THREE.Vector3),void 0===matrix&&(matrix=new THREE.Matrix4);var te=this.elements,sx=vector.set(te[0],te[1],te[2]).length(),sy=vector.set(te[4],te[5],te[6]).length(),sz=vector.set(te[8],te[9],te[10]).length(),det=this.determinant();0>det&&(sx=-sx),position.x=te[12],position.y=te[13],position.z=te[14],matrix.elements.set(this.elements);var invSX=1/sx,invSY=1/sy,invSZ=1/sz;return matrix.elements[0]*=invSX,matrix.elements[1]*=invSX,matrix.elements[2]*=invSX,matrix.elements[4]*=invSY,matrix.elements[5]*=invSY,matrix.elements[6]*=invSY,matrix.elements[8]*=invSZ,matrix.elements[9]*=invSZ,matrix.elements[10]*=invSZ,quaternion.setFromRotationMatrix(matrix),scale.x=sx,scale.y=sy,scale.z=sz,this}}(),makeFrustum:function(left,right,bottom,top,near,far){var te=this.elements,x=2*near/(right-left),y=2*near/(top-bottom),a=(right+left)/(right-left),b=(top+bottom)/(top-bottom),c=-(far+near)/(far-near),d=-2*far*near/(far-near);return te[0]=x,te[4]=0,te[8]=a,te[12]=0,te[1]=0,te[5]=y,te[9]=b,te[13]=0,te[2]=0,te[6]=0,te[10]=c,te[14]=d,te[3]=0,te[7]=0,te[11]=-1,te[15]=0,this},makePerspective:function(fov,aspect,near,far){var ymax=near*Math.tan(THREE.Math.degToRad(.5*fov)),ymin=-ymax,xmin=ymin*aspect,xmax=ymax*aspect;return this.makeFrustum(xmin,xmax,ymin,ymax,near,far)},makeOrthographic:function(left,right,top,bottom,near,far){var te=this.elements,w=right-left,h=top-bottom,p=far-near,x=(right+left)/w,y=(top+bottom)/h,z=(far+near)/p;return te[0]=2/w,te[4]=0,te[8]=0,te[12]=-x,te[1]=0,te[5]=2/h,te[9]=0,te[13]=-y,te[2]=0,te[6]=0,te[10]=-2/p,te[14]=-z,te[3]=0,te[7]=0,te[11]=0,te[15]=1,this},equals:function(matrix){for(var te=this.elements,me=matrix.elements,i=0;16>i;i++)if(te[i]!==me[i])return!1;return!0},fromArray:function(array){return this.elements.set(array),this},toArray:function(){var te=this.elements;return[te[0],te[1],te[2],te[3],te[4],te[5],te[6],te[7],te[8],te[9],te[10],te[11],te[12],te[13],te[14],te[15]]}},THREE.Ray=function(origin,direction){this.origin=void 0!==origin?origin:new THREE.Vector3,this.direction=void 0!==direction?direction:new THREE.Vector3},THREE.Ray.prototype={constructor:THREE.Ray,set:function(origin,direction){return this.origin.copy(origin),this.direction.copy(direction),this},clone:function(){return(new this.constructor).copy(this)},copy:function(ray){return this.origin.copy(ray.origin),this.direction.copy(ray.direction),this},at:function(t,optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.copy(this.direction).multiplyScalar(t).add(this.origin)},recast:function(){var v1=new THREE.Vector3;return function(t){return this.origin.copy(this.at(t,v1)),this}}(),closestPointToPoint:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector3;result.subVectors(point,this.origin);var directionDistance=result.dot(this.direction);return 0>directionDistance?result.copy(this.origin):result.copy(this.direction).multiplyScalar(directionDistance).add(this.origin)},distanceToPoint:function(point){return Math.sqrt(this.distanceSqToPoint(point))},distanceSqToPoint:function(){var v1=new THREE.Vector3;return function(point){var directionDistance=v1.subVectors(point,this.origin).dot(this.direction);return 0>directionDistance?this.origin.distanceToSquared(point):(v1.copy(this.direction).multiplyScalar(directionDistance).add(this.origin),v1.distanceToSquared(point))}}(),distanceSqToSegment:function(){var segCenter=new THREE.Vector3,segDir=new THREE.Vector3,diff=new THREE.Vector3;return function(v0,v1,optionalPointOnRay,optionalPointOnSegment){segCenter.copy(v0).add(v1).multiplyScalar(.5),segDir.copy(v1).sub(v0).normalize(),diff.copy(this.origin).sub(segCenter);var s0,s1,sqrDist,extDet,segExtent=.5*v0.distanceTo(v1),a01=-this.direction.dot(segDir),b0=diff.dot(this.direction),b1=-diff.dot(segDir),c=diff.lengthSq(),det=Math.abs(1-a01*a01);if(det>0)if(s0=a01*b1-b0,s1=a01*b0-b1,extDet=segExtent*det,s0>=0)if(s1>=-extDet)if(extDet>=s1){var invDet=1/det;s0*=invDet,s1*=invDet,sqrDist=s0*(s0+a01*s1+2*b0)+s1*(a01*s0+s1+2*b1)+c}else s1=segExtent,s0=Math.max(0,-(a01*s1+b0)),sqrDist=-s0*s0+s1*(s1+2*b1)+c;else s1=-segExtent,s0=Math.max(0,-(a01*s1+b0)),sqrDist=-s0*s0+s1*(s1+2*b1)+c;else-extDet>=s1?(s0=Math.max(0,-(-a01*segExtent+b0)),s1=s0>0?-segExtent:Math.min(Math.max(-segExtent,-b1),segExtent),sqrDist=-s0*s0+s1*(s1+2*b1)+c):extDet>=s1?(s0=0,s1=Math.min(Math.max(-segExtent,-b1),segExtent),sqrDist=s1*(s1+2*b1)+c):(s0=Math.max(0,-(a01*segExtent+b0)),s1=s0>0?segExtent:Math.min(Math.max(-segExtent,-b1),segExtent),sqrDist=-s0*s0+s1*(s1+2*b1)+c);else s1=a01>0?-segExtent:segExtent,s0=Math.max(0,-(a01*s1+b0)),sqrDist=-s0*s0+s1*(s1+2*b1)+c;return optionalPointOnRay&&optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin),optionalPointOnSegment&&optionalPointOnSegment.copy(segDir).multiplyScalar(s1).add(segCenter),sqrDist}}(),isIntersectionSphere:function(sphere){return this.distanceToPoint(sphere.center)<=sphere.radius},intersectSphere:function(){var v1=new THREE.Vector3;return function(sphere,optionalTarget){v1.subVectors(sphere.center,this.origin);var tca=v1.dot(this.direction),d2=v1.dot(v1)-tca*tca,radius2=sphere.radius*sphere.radius;if(d2>radius2)return null;var thc=Math.sqrt(radius2-d2),t0=tca-thc,t1=tca+thc;return 0>t0&&0>t1?null:0>t0?this.at(t1,optionalTarget):this.at(t0,optionalTarget)}}(),isIntersectionPlane:function(plane){var distToPoint=plane.distanceToPoint(this.origin);if(0===distToPoint)return!0;var denominator=plane.normal.dot(this.direction);return 0>denominator*distToPoint},distanceToPlane:function(plane){var denominator=plane.normal.dot(this.direction);if(0===denominator)return 0===plane.distanceToPoint(this.origin)?0:null;var t=-(this.origin.dot(plane.normal)+plane.constant)/denominator;return t>=0?t:null},intersectPlane:function(plane,optionalTarget){var t=this.distanceToPlane(plane);return null===t?null:this.at(t,optionalTarget)},isIntersectionBox:function(){var v=new THREE.Vector3;return function(box){return null!==this.intersectBox(box,v)}}(),intersectBox:function(box,optionalTarget){var tmin,tmax,tymin,tymax,tzmin,tzmax,invdirx=1/this.direction.x,invdiry=1/this.direction.y,invdirz=1/this.direction.z,origin=this.origin;return invdirx>=0?(tmin=(box.min.x-origin.x)*invdirx,tmax=(box.max.x-origin.x)*invdirx):(tmin=(box.max.x-origin.x)*invdirx,tmax=(box.min.x-origin.x)*invdirx),invdiry>=0?(tymin=(box.min.y-origin.y)*invdiry,tymax=(box.max.y-origin.y)*invdiry):(tymin=(box.max.y-origin.y)*invdiry,tymax=(box.min.y-origin.y)*invdiry),tmin>tymax||tymin>tmax?null:((tymin>tmin||tmin!==tmin)&&(tmin=tymin),(tmax>tymax||tmax!==tmax)&&(tmax=tymax),invdirz>=0?(tzmin=(box.min.z-origin.z)*invdirz,tzmax=(box.max.z-origin.z)*invdirz):(tzmin=(box.max.z-origin.z)*invdirz,tzmax=(box.min.z-origin.z)*invdirz),tmin>tzmax||tzmin>tmax?null:((tzmin>tmin||tmin!==tmin)&&(tmin=tzmin),(tmax>tzmax||tmax!==tmax)&&(tmax=tzmax),0>tmax?null:this.at(tmin>=0?tmin:tmax,optionalTarget)))},intersectTriangle:function(){var diff=new THREE.Vector3,edge1=new THREE.Vector3,edge2=new THREE.Vector3,normal=new THREE.Vector3;return function(a,b,c,backfaceCulling,optionalTarget){edge1.subVectors(b,a),edge2.subVectors(c,a),normal.crossVectors(edge1,edge2);var sign,DdN=this.direction.dot(normal);if(DdN>0){if(backfaceCulling)return null;sign=1}else{if(!(0>DdN))return null;sign=-1,DdN=-DdN}diff.subVectors(this.origin,a);var DdQxE2=sign*this.direction.dot(edge2.crossVectors(diff,edge2));if(0>DdQxE2)return null;var DdE1xQ=sign*this.direction.dot(edge1.cross(diff));if(0>DdE1xQ)return null;if(DdQxE2+DdE1xQ>DdN)return null;var QdN=-sign*diff.dot(normal);return 0>QdN?null:this.at(QdN/DdN,optionalTarget)}}(),applyMatrix4:function(matrix4){return this.direction.add(this.origin).applyMatrix4(matrix4),this.origin.applyMatrix4(matrix4),this.direction.sub(this.origin),this.direction.normalize(),this},equals:function(ray){return ray.origin.equals(this.origin)&&ray.direction.equals(this.direction)}},THREE.Sphere=function(center,radius){this.center=void 0!==center?center:new THREE.Vector3,this.radius=void 0!==radius?radius:0},THREE.Sphere.prototype={constructor:THREE.Sphere,set:function(center,radius){return this.center.copy(center),this.radius=radius,this},setFromPoints:function(){var box=new THREE.Box3;return function(points,optionalCenter){var center=this.center;void 0!==optionalCenter?center.copy(optionalCenter):box.setFromPoints(points).center(center);for(var maxRadiusSq=0,i=0,il=points.length;il>i;i++)maxRadiusSq=Math.max(maxRadiusSq,center.distanceToSquared(points[i]));return this.radius=Math.sqrt(maxRadiusSq),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(sphere){return this.center.copy(sphere.center),this.radius=sphere.radius,this},empty:function(){return this.radius<=0},containsPoint:function(point){return point.distanceToSquared(this.center)<=this.radius*this.radius},distanceToPoint:function(point){return point.distanceTo(this.center)-this.radius},intersectsSphere:function(sphere){var radiusSum=this.radius+sphere.radius;return sphere.center.distanceToSquared(this.center)<=radiusSum*radiusSum},clampPoint:function(point,optionalTarget){var deltaLengthSq=this.center.distanceToSquared(point),result=optionalTarget||new THREE.Vector3;return result.copy(point),deltaLengthSq>this.radius*this.radius&&(result.sub(this.center).normalize(),result.multiplyScalar(this.radius).add(this.center)),result},getBoundingBox:function(optionalTarget){var box=optionalTarget||new THREE.Box3;return box.set(this.center,this.center),box.expandByScalar(this.radius),box},applyMatrix4:function(matrix){return this.center.applyMatrix4(matrix),this.radius=this.radius*matrix.getMaxScaleOnAxis(),this},translate:function(offset){return this.center.add(offset),this},equals:function(sphere){return sphere.center.equals(this.center)&&sphere.radius===this.radius}},THREE.Frustum=function(p0,p1,p2,p3,p4,p5){this.planes=[void 0!==p0?p0:new THREE.Plane,void 0!==p1?p1:new THREE.Plane,void 0!==p2?p2:new THREE.Plane,void 0!==p3?p3:new THREE.Plane,void 0!==p4?p4:new THREE.Plane,void 0!==p5?p5:new THREE.Plane]},THREE.Frustum.prototype={constructor:THREE.Frustum,set:function(p0,p1,p2,p3,p4,p5){var planes=this.planes;return planes[0].copy(p0),planes[1].copy(p1),planes[2].copy(p2),planes[3].copy(p3),planes[4].copy(p4),planes[5].copy(p5),this},clone:function(){return(new this.constructor).copy(this)},copy:function(frustum){for(var planes=this.planes,i=0;6>i;i++)planes[i].copy(frustum.planes[i]);return this},setFromMatrix:function(m){var planes=this.planes,me=m.elements,me0=me[0],me1=me[1],me2=me[2],me3=me[3],me4=me[4],me5=me[5],me6=me[6],me7=me[7],me8=me[8],me9=me[9],me10=me[10],me11=me[11],me12=me[12],me13=me[13],me14=me[14],me15=me[15];return planes[0].setComponents(me3-me0,me7-me4,me11-me8,me15-me12).normalize(),planes[1].setComponents(me3+me0,me7+me4,me11+me8,me15+me12).normalize(),planes[2].setComponents(me3+me1,me7+me5,me11+me9,me15+me13).normalize(),planes[3].setComponents(me3-me1,me7-me5,me11-me9,me15-me13).normalize(),planes[4].setComponents(me3-me2,me7-me6,me11-me10,me15-me14).normalize(),planes[5].setComponents(me3+me2,me7+me6,me11+me10,me15+me14).normalize(),this},intersectsObject:function(){var sphere=new THREE.Sphere;return function(object){var geometry=object.geometry;return null===geometry.boundingSphere&&geometry.computeBoundingSphere(),sphere.copy(geometry.boundingSphere),sphere.applyMatrix4(object.matrixWorld),this.intersectsSphere(sphere)}}(),intersectsSphere:function(sphere){for(var planes=this.planes,center=sphere.center,negRadius=-sphere.radius,i=0;6>i;i++){var distance=planes[i].distanceToPoint(center);if(negRadius>distance)return!1}return!0},intersectsBox:function(){var p1=new THREE.Vector3,p2=new THREE.Vector3;return function(box){for(var planes=this.planes,i=0;6>i;i++){var plane=planes[i];p1.x=plane.normal.x>0?box.min.x:box.max.x,p2.x=plane.normal.x>0?box.max.x:box.min.x,p1.y=plane.normal.y>0?box.min.y:box.max.y,p2.y=plane.normal.y>0?box.max.y:box.min.y,p1.z=plane.normal.z>0?box.min.z:box.max.z,p2.z=plane.normal.z>0?box.max.z:box.min.z;var d1=plane.distanceToPoint(p1),d2=plane.distanceToPoint(p2);if(0>d1&&0>d2)return!1}return!0}}(),containsPoint:function(point){for(var planes=this.planes,i=0;6>i;i++)if(planes[i].distanceToPoint(point)<0)return!1;return!0}},THREE.Plane=function(normal,constant){this.normal=void 0!==normal?normal:new THREE.Vector3(1,0,0),this.constant=void 0!==constant?constant:0},THREE.Plane.prototype={constructor:THREE.Plane,set:function(normal,constant){return this.normal.copy(normal),this.constant=constant,this},setComponents:function(x,y,z,w){return this.normal.set(x,y,z),this.constant=w,this},setFromNormalAndCoplanarPoint:function(normal,point){return this.normal.copy(normal),this.constant=-point.dot(this.normal),this},setFromCoplanarPoints:function(){var v1=new THREE.Vector3,v2=new THREE.Vector3;return function(a,b,c){var normal=v1.subVectors(c,b).cross(v2.subVectors(a,b)).normalize();return this.setFromNormalAndCoplanarPoint(normal,a),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(plane){return this.normal.copy(plane.normal),this.constant=plane.constant,this},normalize:function(){var inverseNormalLength=1/this.normal.length();return this.normal.multiplyScalar(inverseNormalLength),this.constant*=inverseNormalLength,this},negate:function(){return this.constant*=-1,this.normal.negate(),this},distanceToPoint:function(point){return this.normal.dot(point)+this.constant},distanceToSphere:function(sphere){return this.distanceToPoint(sphere.center)-sphere.radius},projectPoint:function(point,optionalTarget){return this.orthoPoint(point,optionalTarget).sub(point).negate()},orthoPoint:function(point,optionalTarget){var perpendicularMagnitude=this.distanceToPoint(point),result=optionalTarget||new THREE.Vector3;return result.copy(this.normal).multiplyScalar(perpendicularMagnitude)},isIntersectionLine:function(line){var startSign=this.distanceToPoint(line.start),endSign=this.distanceToPoint(line.end);return 0>startSign&&endSign>0||0>endSign&&startSign>0},intersectLine:function(){var v1=new THREE.Vector3;return function(line,optionalTarget){var result=optionalTarget||new THREE.Vector3,direction=line.delta(v1),denominator=this.normal.dot(direction);if(0!==denominator){var t=-(line.start.dot(this.normal)+this.constant)/denominator;if(!(0>t||t>1))return result.copy(direction).multiplyScalar(t).add(line.start);
+}else if(0===this.distanceToPoint(line.start))return result.copy(line.start)}}(),coplanarPoint:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.copy(this.normal).multiplyScalar(-this.constant)},applyMatrix4:function(){var v1=new THREE.Vector3,v2=new THREE.Vector3,m1=new THREE.Matrix3;return function(matrix,optionalNormalMatrix){var normalMatrix=optionalNormalMatrix||m1.getNormalMatrix(matrix),newNormal=v1.copy(this.normal).applyMatrix3(normalMatrix),newCoplanarPoint=this.coplanarPoint(v2);return newCoplanarPoint.applyMatrix4(matrix),this.setFromNormalAndCoplanarPoint(newNormal,newCoplanarPoint),this}}(),translate:function(offset){return this.constant=this.constant-offset.dot(this.normal),this},equals:function(plane){return plane.normal.equals(this.normal)&&plane.constant===this.constant}},THREE.Math={generateUUID:function(){var r,chars="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),uuid=new Array(36),rnd=0;return function(){for(var i=0;36>i;i++)8===i||13===i||18===i||23===i?uuid[i]="-":14===i?uuid[i]="4":(2>=rnd&&(rnd=33554432+16777216*Math.random()|0),r=15&rnd,rnd>>=4,uuid[i]=chars[19===i?3&r|8:r]);return uuid.join("")}}(),clamp:function(value,min,max){return Math.max(min,Math.min(max,value))},euclideanModulo:function(n,m){return(n%m+m)%m},mapLinear:function(x,a1,a2,b1,b2){return b1+(x-a1)*(b2-b1)/(a2-a1)},smoothstep:function(x,min,max){return min>=x?0:x>=max?1:(x=(x-min)/(max-min),x*x*(3-2*x))},smootherstep:function(x,min,max){return min>=x?0:x>=max?1:(x=(x-min)/(max-min),x*x*x*(x*(6*x-15)+10))},random16:function(){return(65280*Math.random()+255*Math.random())/65535},randInt:function(low,high){return low+Math.floor(Math.random()*(high-low+1))},randFloat:function(low,high){return low+Math.random()*(high-low)},randFloatSpread:function(range){return range*(.5-Math.random())},degToRad:function(){var degreeToRadiansFactor=Math.PI/180;return function(degrees){return degrees*degreeToRadiansFactor}}(),radToDeg:function(){var radianToDegreesFactor=180/Math.PI;return function(radians){return radians*radianToDegreesFactor}}(),isPowerOfTwo:function(value){return 0===(value&value-1)&&0!==value},nearestPowerOfTwo:function(value){return Math.pow(2,Math.round(Math.log(value)/Math.LN2))},nextPowerOfTwo:function(value){return value--,value|=value>>1,value|=value>>2,value|=value>>4,value|=value>>8,value|=value>>16,value++,value}},THREE.Spline=function(points){function interpolate(p0,p1,p2,p3,t,t2,t3){var v0=.5*(p2-p0),v1=.5*(p3-p1);return(2*(p1-p2)+v0+v1)*t3+(-3*(p1-p2)-2*v0-v1)*t2+v0*t+p1}this.points=points;var point,intPoint,weight,w2,w3,pa,pb,pc,pd,c=[],v3={x:0,y:0,z:0};this.initFromArray=function(a){this.points=[];for(var i=0;ithis.points.length-2?this.points.length-1:intPoint+1,c[3]=intPoint>this.points.length-3?this.points.length-1:intPoint+2,pa=this.points[c[0]],pb=this.points[c[1]],pc=this.points[c[2]],pd=this.points[c[3]],w2=weight*weight,w3=weight*w2,v3.x=interpolate(pa.x,pb.x,pc.x,pd.x,weight,w2,w3),v3.y=interpolate(pa.y,pb.y,pc.y,pd.y,weight,w2,w3),v3.z=interpolate(pa.z,pb.z,pc.z,pd.z,weight,w2,w3),v3},this.getControlPointsArray=function(){var i,p,l=this.points.length,coords=[];for(i=0;l>i;i++)p=this.points[i],coords[i]=[p.x,p.y,p.z];return coords},this.getLength=function(nSubDivisions){var i,index,nSamples,position,point=0,intPoint=0,oldIntPoint=0,oldPosition=new THREE.Vector3,tmpVec=new THREE.Vector3,chunkLengths=[],totalLength=0;for(chunkLengths[0]=0,nSubDivisions||(nSubDivisions=100),nSamples=this.points.length*nSubDivisions,oldPosition.copy(this.points[0]),i=1;nSamples>i;i++)index=i/nSamples,position=this.getPoint(index),tmpVec.copy(position),totalLength+=tmpVec.distanceTo(oldPosition),oldPosition.copy(position),point=(this.points.length-1)*index,intPoint=Math.floor(point),intPoint!==oldIntPoint&&(chunkLengths[intPoint]=totalLength,oldIntPoint=intPoint);return chunkLengths[chunkLengths.length]=totalLength,{chunks:chunkLengths,total:totalLength}},this.reparametrizeByArcLength=function(samplingCoef){var i,j,index,indexCurrent,indexNext,realDistance,sampling,position,newpoints=[],tmpVec=new THREE.Vector3,sl=this.getLength();for(newpoints.push(tmpVec.copy(this.points[0]).clone()),i=1;ij;j++)index=indexCurrent+j*(1/sampling)*(indexNext-indexCurrent),position=this.getPoint(index),newpoints.push(tmpVec.copy(position).clone());newpoints.push(tmpVec.copy(this.points[i]).clone())}this.points=newpoints}},THREE.Triangle=function(a,b,c){this.a=void 0!==a?a:new THREE.Vector3,this.b=void 0!==b?b:new THREE.Vector3,this.c=void 0!==c?c:new THREE.Vector3},THREE.Triangle.normal=function(){var v0=new THREE.Vector3;return function(a,b,c,optionalTarget){var result=optionalTarget||new THREE.Vector3;result.subVectors(c,b),v0.subVectors(a,b),result.cross(v0);var resultLengthSq=result.lengthSq();return resultLengthSq>0?result.multiplyScalar(1/Math.sqrt(resultLengthSq)):result.set(0,0,0)}}(),THREE.Triangle.barycoordFromPoint=function(){var v0=new THREE.Vector3,v1=new THREE.Vector3,v2=new THREE.Vector3;return function(point,a,b,c,optionalTarget){v0.subVectors(c,a),v1.subVectors(b,a),v2.subVectors(point,a);var dot00=v0.dot(v0),dot01=v0.dot(v1),dot02=v0.dot(v2),dot11=v1.dot(v1),dot12=v1.dot(v2),denom=dot00*dot11-dot01*dot01,result=optionalTarget||new THREE.Vector3;if(0===denom)return result.set(-2,-1,-1);var invDenom=1/denom,u=(dot11*dot02-dot01*dot12)*invDenom,v=(dot00*dot12-dot01*dot02)*invDenom;return result.set(1-u-v,v,u)}}(),THREE.Triangle.containsPoint=function(){var v1=new THREE.Vector3;return function(point,a,b,c){var result=THREE.Triangle.barycoordFromPoint(point,a,b,c,v1);return result.x>=0&&result.y>=0&&result.x+result.y<=1}}(),THREE.Triangle.prototype={constructor:THREE.Triangle,set:function(a,b,c){return this.a.copy(a),this.b.copy(b),this.c.copy(c),this},setFromPointsAndIndices:function(points,i0,i1,i2){return this.a.copy(points[i0]),this.b.copy(points[i1]),this.c.copy(points[i2]),this},clone:function(){return(new this.constructor).copy(this)},copy:function(triangle){return this.a.copy(triangle.a),this.b.copy(triangle.b),this.c.copy(triangle.c),this},area:function(){var v0=new THREE.Vector3,v1=new THREE.Vector3;return function(){return v0.subVectors(this.c,this.b),v1.subVectors(this.a,this.b),.5*v0.cross(v1).length()}}(),midpoint:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},normal:function(optionalTarget){return THREE.Triangle.normal(this.a,this.b,this.c,optionalTarget)},plane:function(optionalTarget){var result=optionalTarget||new THREE.Plane;return result.setFromCoplanarPoints(this.a,this.b,this.c)},barycoordFromPoint:function(point,optionalTarget){return THREE.Triangle.barycoordFromPoint(point,this.a,this.b,this.c,optionalTarget)},containsPoint:function(point){return THREE.Triangle.containsPoint(point,this.a,this.b,this.c)},equals:function(triangle){return triangle.a.equals(this.a)&&triangle.b.equals(this.b)&&triangle.c.equals(this.c)}},THREE.Channels=function(){this.mask=1},THREE.Channels.prototype={constructor:THREE.Channels,set:function(channel){this.mask=1<i;i++)array[i]=listenerArray[i];for(var i=0;length>i;i++)array[i].call(this,event)}}}},function(THREE){function descSort(a,b){return a.distance-b.distance}function intersectObject(object,raycaster,intersects,recursive){if(object.visible!==!1&&(object.raycast(raycaster,intersects),recursive===!0))for(var children=object.children,i=0,l=children.length;l>i;i++)intersectObject(children[i],raycaster,intersects,!0)}THREE.Raycaster=function(origin,direction,near,far){this.ray=new THREE.Ray(origin,direction),this.near=near||0,this.far=far||1/0,this.params={Mesh:{},Line:{},LOD:{},Points:{threshold:1},Sprite:{}},Object.defineProperties(this.params,{PointCloud:{get:function(){return console.warn("THREE.Raycaster: params.PointCloud has been renamed to params.Points."),this.Points}}})},THREE.Raycaster.prototype={constructor:THREE.Raycaster,linePrecision:1,set:function(origin,direction){this.ray.set(origin,direction)},setFromCamera:function(coords,camera){camera instanceof THREE.PerspectiveCamera?(this.ray.origin.setFromMatrixPosition(camera.matrixWorld),this.ray.direction.set(coords.x,coords.y,.5).unproject(camera).sub(this.ray.origin).normalize()):camera instanceof THREE.OrthographicCamera?(this.ray.origin.set(coords.x,coords.y,-1).unproject(camera),this.ray.direction.set(0,0,-1).transformDirection(camera.matrixWorld)):console.error("THREE.Raycaster: Unsupported camera type.")},intersectObject:function(object,recursive){var intersects=[];return intersectObject(object,this,intersects,recursive),intersects.sort(descSort),intersects},intersectObjects:function(objects,recursive){var intersects=[];if(Array.isArray(objects)===!1)return console.warn("THREE.Raycaster.intersectObjects: objects is not an Array."),intersects;for(var i=0,l=objects.length;l>i;i++)intersectObject(objects[i],this,intersects,recursive);return intersects.sort(descSort),intersects}}}(THREE),THREE.Object3D=function(){function onRotationChange(){quaternion.setFromEuler(rotation,!1)}function onQuaternionChange(){rotation.setFromQuaternion(quaternion,void 0,!1)}Object.defineProperty(this,"id",{value:THREE.Object3DIdCount++}),this.uuid=THREE.Math.generateUUID(),this.name="",this.type="Object3D",this.parent=null,this.channels=new THREE.Channels,this.children=[],this.up=THREE.Object3D.DefaultUp.clone();var position=new THREE.Vector3,rotation=new THREE.Euler,quaternion=new THREE.Quaternion,scale=new THREE.Vector3(1,1,1);rotation.onChange(onRotationChange),quaternion.onChange(onQuaternionChange),Object.defineProperties(this,{position:{enumerable:!0,value:position},rotation:{enumerable:!0,value:rotation},quaternion:{enumerable:!0,value:quaternion},scale:{enumerable:!0,value:scale},modelViewMatrix:{value:new THREE.Matrix4},normalMatrix:{value:new THREE.Matrix3}}),this.rotationAutoUpdate=!0,this.matrix=new THREE.Matrix4,this.matrixWorld=new THREE.Matrix4,this.matrixAutoUpdate=THREE.Object3D.DefaultMatrixAutoUpdate,this.matrixWorldNeedsUpdate=!1,this.visible=!0,this.castShadow=!1,this.receiveShadow=!1,this.frustumCulled=!0,this.renderOrder=0,this.userData={}},THREE.Object3D.DefaultUp=new THREE.Vector3(0,1,0),THREE.Object3D.DefaultMatrixAutoUpdate=!0,THREE.Object3D.prototype={constructor:THREE.Object3D,get eulerOrder(){return console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."),this.rotation.order},set eulerOrder(value){console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."),this.rotation.order=value},get useQuaternion(){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},set useQuaternion(value){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},set renderDepth(value){console.warn("THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.")},applyMatrix:function(matrix){this.matrix.multiplyMatrices(matrix,this.matrix),this.matrix.decompose(this.position,this.quaternion,this.scale)},setRotationFromAxisAngle:function(axis,angle){this.quaternion.setFromAxisAngle(axis,angle)},setRotationFromEuler:function(euler){this.quaternion.setFromEuler(euler,!0)},setRotationFromMatrix:function(m){this.quaternion.setFromRotationMatrix(m)},setRotationFromQuaternion:function(q){this.quaternion.copy(q)},rotateOnAxis:function(){var q1=new THREE.Quaternion;return function(axis,angle){return q1.setFromAxisAngle(axis,angle),this.quaternion.multiply(q1),this}}(),rotateX:function(){var v1=new THREE.Vector3(1,0,0);return function(angle){return this.rotateOnAxis(v1,angle)}}(),rotateY:function(){var v1=new THREE.Vector3(0,1,0);return function(angle){return this.rotateOnAxis(v1,angle)}}(),rotateZ:function(){var v1=new THREE.Vector3(0,0,1);return function(angle){return this.rotateOnAxis(v1,angle)}}(),translateOnAxis:function(){var v1=new THREE.Vector3;return function(axis,distance){return v1.copy(axis).applyQuaternion(this.quaternion),this.position.add(v1.multiplyScalar(distance)),this}}(),translate:function(distance,axis){return console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead."),this.translateOnAxis(axis,distance)},translateX:function(){var v1=new THREE.Vector3(1,0,0);return function(distance){return this.translateOnAxis(v1,distance)}}(),translateY:function(){var v1=new THREE.Vector3(0,1,0);return function(distance){return this.translateOnAxis(v1,distance)}}(),translateZ:function(){var v1=new THREE.Vector3(0,0,1);return function(distance){return this.translateOnAxis(v1,distance)}}(),localToWorld:function(vector){return vector.applyMatrix4(this.matrixWorld)},worldToLocal:function(){var m1=new THREE.Matrix4;return function(vector){return vector.applyMatrix4(m1.getInverse(this.matrixWorld))}}(),lookAt:function(){var m1=new THREE.Matrix4;return function(vector){m1.lookAt(vector,this.position,this.up),this.quaternion.setFromRotationMatrix(m1)}}(),add:function(object){if(arguments.length>1){for(var i=0;i1)for(var i=0;ii;i++){var child=this.children[i],object=child.getObjectByProperty(name,value);if(void 0!==object)return object}},getWorldPosition:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.updateMatrixWorld(!0),result.setFromMatrixPosition(this.matrixWorld)},getWorldQuaternion:function(){var position=new THREE.Vector3,scale=new THREE.Vector3;return function(optionalTarget){var result=optionalTarget||new THREE.Quaternion;return this.updateMatrixWorld(!0),this.matrixWorld.decompose(position,result,scale),result}}(),getWorldRotation:function(){var quaternion=new THREE.Quaternion;return function(optionalTarget){var result=optionalTarget||new THREE.Euler;return this.getWorldQuaternion(quaternion),result.setFromQuaternion(quaternion,this.rotation.order,!1)}}(),getWorldScale:function(){var position=new THREE.Vector3,quaternion=new THREE.Quaternion;return function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.updateMatrixWorld(!0),this.matrixWorld.decompose(position,quaternion,result),result}}(),getWorldDirection:function(){var quaternion=new THREE.Quaternion;return function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.getWorldQuaternion(quaternion),result.set(0,0,1).applyQuaternion(quaternion)}}(),raycast:function(){},traverse:function(callback){callback(this);for(var children=this.children,i=0,l=children.length;l>i;i++)children[i].traverse(callback)},traverseVisible:function(callback){if(this.visible!==!1){callback(this);for(var children=this.children,i=0,l=children.length;l>i;i++)children[i].traverseVisible(callback)}},traverseAncestors:function(callback){var parent=this.parent;null!==parent&&(callback(parent),parent.traverseAncestors(callback))},updateMatrix:function(){this.matrix.compose(this.position,this.quaternion,this.scale),this.matrixWorldNeedsUpdate=!0},updateMatrixWorld:function(force){this.matrixAutoUpdate===!0&&this.updateMatrix(),this.matrixWorldNeedsUpdate!==!0&&force!==!0||(null===this.parent?this.matrixWorld.copy(this.matrix):this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix),this.matrixWorldNeedsUpdate=!1,force=!0);for(var i=0,l=this.children.length;l>i;i++)this.children[i].updateMatrixWorld(force)},toJSON:function(meta){function extractFromCache(cache){var values=[];for(var key in cache){var data=cache[key];delete data.metadata,values.push(data)}return values}var isRootObject=void 0===meta,output={};isRootObject&&(meta={geometries:{},materials:{},textures:{},images:{}},output.metadata={version:4.4,type:"Object",generator:"Object3D.toJSON"});var object={};if(object.uuid=this.uuid,object.type=this.type,""!==this.name&&(object.name=this.name),"{}"!==JSON.stringify(this.userData)&&(object.userData=this.userData),this.castShadow===!0&&(object.castShadow=!0),this.receiveShadow===!0&&(object.receiveShadow=!0),this.visible===!1&&(object.visible=!1),object.matrix=this.matrix.toArray(),void 0!==this.geometry&&(void 0===meta.geometries[this.geometry.uuid]&&(meta.geometries[this.geometry.uuid]=this.geometry.toJSON(meta)),object.geometry=this.geometry.uuid),void 0!==this.material&&(void 0===meta.materials[this.material.uuid]&&(meta.materials[this.material.uuid]=this.material.toJSON(meta)),object.material=this.material.uuid),this.children.length>0){object.children=[];for(var i=0;i0&&(output.geometries=geometries),materials.length>0&&(output.materials=materials),textures.length>0&&(output.textures=textures),images.length>0&&(output.images=images)}return output.object=object,output},clone:function(recursive){return(new this.constructor).copy(this,recursive)},copy:function(source,recursive){if(void 0===recursive&&(recursive=!0),this.name=source.name,this.up.copy(source.up),this.position.copy(source.position),this.quaternion.copy(source.quaternion),this.scale.copy(source.scale),this.rotationAutoUpdate=source.rotationAutoUpdate,this.matrix.copy(source.matrix),this.matrixWorld.copy(source.matrixWorld),this.matrixAutoUpdate=source.matrixAutoUpdate,this.matrixWorldNeedsUpdate=source.matrixWorldNeedsUpdate,this.visible=source.visible,this.castShadow=source.castShadow,this.receiveShadow=source.receiveShadow,this.frustumCulled=source.frustumCulled,this.renderOrder=source.renderOrder,this.userData=JSON.parse(JSON.stringify(source.userData)),recursive===!0)for(var i=0;ii;i++)this.vertexNormals[i]=source.vertexNormals[i].clone();for(var i=0,il=source.vertexColors.length;il>i;i++)this.vertexColors[i]=source.vertexColors[i].clone();return this}},THREE.Face4=function(a,b,c,d,normal,color,materialIndex){return console.warn("THREE.Face4 has been removed. A THREE.Face3 will be created instead."),new THREE.Face3(a,b,c,normal,color,materialIndex)},THREE.BufferAttribute=function(array,itemSize){this.uuid=THREE.Math.generateUUID(),this.array=array,this.itemSize=itemSize,this.dynamic=!1,this.updateRange={offset:0,count:-1},this.version=0},THREE.BufferAttribute.prototype={constructor:THREE.BufferAttribute,get length(){return console.warn("THREE.BufferAttribute: .length has been deprecated. Please use .count."),this.array.length},get count(){return this.array.length/this.itemSize},set needsUpdate(value){value===!0&&this.version++},setDynamic:function(value){return this.dynamic=value,this},copy:function(source){return this.array=new source.array.constructor(source.array),this.itemSize=source.itemSize,this.dynamic=source.dynamic,this},copyAt:function(index1,attribute,index2){index1*=this.itemSize,index2*=attribute.itemSize;for(var i=0,l=this.itemSize;l>i;i++)this.array[index1+i]=attribute.array[index2+i];return this},copyArray:function(array){return this.array.set(array),this},copyColorsArray:function(colors){for(var array=this.array,offset=0,i=0,l=colors.length;l>i;i++){var color=colors[i];void 0===color&&(console.warn("THREE.BufferAttribute.copyColorsArray(): color is undefined",i),color=new THREE.Color),array[offset++]=color.r,array[offset++]=color.g,array[offset++]=color.b}return this},copyIndicesArray:function(indices){for(var array=this.array,offset=0,i=0,l=indices.length;l>i;i++){var index=indices[i];array[offset++]=index.a,array[offset++]=index.b,array[offset++]=index.c}return this},copyVector2sArray:function(vectors){for(var array=this.array,offset=0,i=0,l=vectors.length;l>i;i++){var vector=vectors[i];void 0===vector&&(console.warn("THREE.BufferAttribute.copyVector2sArray(): vector is undefined",i),vector=new THREE.Vector2),array[offset++]=vector.x,array[offset++]=vector.y}return this},copyVector3sArray:function(vectors){for(var array=this.array,offset=0,i=0,l=vectors.length;l>i;i++){var vector=vectors[i];void 0===vector&&(console.warn("THREE.BufferAttribute.copyVector3sArray(): vector is undefined",i),vector=new THREE.Vector3),array[offset++]=vector.x,array[offset++]=vector.y,array[offset++]=vector.z}return this},copyVector4sArray:function(vectors){for(var array=this.array,offset=0,i=0,l=vectors.length;l>i;i++){var vector=vectors[i];void 0===vector&&(console.warn("THREE.BufferAttribute.copyVector4sArray(): vector is undefined",i),vector=new THREE.Vector4),array[offset++]=vector.x,array[offset++]=vector.y,array[offset++]=vector.z,array[offset++]=vector.w}return this},set:function(value,offset){return void 0===offset&&(offset=0),this.array.set(value,offset),this},getX:function(index){return this.array[index*this.itemSize]},setX:function(index,x){return this.array[index*this.itemSize]=x,this},getY:function(index){return this.array[index*this.itemSize+1]},setY:function(index,y){return this.array[index*this.itemSize+1]=y,this},getZ:function(index){return this.array[index*this.itemSize+2]},setZ:function(index,z){return this.array[index*this.itemSize+2]=z,this},getW:function(index){return this.array[index*this.itemSize+3]},setW:function(index,w){return this.array[index*this.itemSize+3]=w,this},setXY:function(index,x,y){return index*=this.itemSize,this.array[index+0]=x,this.array[index+1]=y,this},setXYZ:function(index,x,y,z){return index*=this.itemSize,this.array[index+0]=x,this.array[index+1]=y,this.array[index+2]=z,this},setXYZW:function(index,x,y,z,w){return index*=this.itemSize,this.array[index+0]=x,this.array[index+1]=y,this.array[index+2]=z,this.array[index+3]=w,this},clone:function(){return(new this.constructor).copy(this)}},THREE.Int8Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Int8Array(array),itemSize)},THREE.Uint8Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint8Array(array),itemSize)},THREE.Uint8ClampedAttribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint8ClampedArray(array),itemSize)},THREE.Int16Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Int16Array(array),itemSize)},THREE.Uint16Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint16Array(array),itemSize)},THREE.Int32Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Int32Array(array),itemSize)},THREE.Uint32Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint32Array(array),itemSize)},THREE.Float32Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Float32Array(array),itemSize)},THREE.Float64Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Float64Array(array),itemSize)},THREE.DynamicBufferAttribute=function(array,itemSize){return console.warn("THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead."),new THREE.BufferAttribute(array,itemSize).setDynamic(!0)},THREE.InstancedBufferAttribute=function(array,itemSize,meshPerAttribute){THREE.BufferAttribute.call(this,array,itemSize),this.meshPerAttribute=meshPerAttribute||1},THREE.InstancedBufferAttribute.prototype=Object.create(THREE.BufferAttribute.prototype),THREE.InstancedBufferAttribute.prototype.constructor=THREE.InstancedBufferAttribute,THREE.InstancedBufferAttribute.prototype.copy=function(source){return THREE.BufferAttribute.prototype.copy.call(this,source),this.meshPerAttribute=source.meshPerAttribute,this},THREE.InterleavedBuffer=function(array,stride){this.uuid=THREE.Math.generateUUID(),this.array=array,this.stride=stride,this.dynamic=!1,this.updateRange={offset:0,count:-1},this.version=0},THREE.InterleavedBuffer.prototype={constructor:THREE.InterleavedBuffer,get length(){return this.array.length},get count(){return this.array.length/this.stride},set needsUpdate(value){value===!0&&this.version++},setDynamic:function(value){return this.dynamic=value,this},copy:function(source){this.array=new source.array.constructor(source.array),this.stride=source.stride,this.dynamic=source.dynamic},copyAt:function(index1,attribute,index2){index1*=this.stride,index2*=attribute.stride;for(var i=0,l=this.stride;l>i;i++)this.array[index1+i]=attribute.array[index2+i];return this},set:function(value,offset){return void 0===offset&&(offset=0),this.array.set(value,offset),this},clone:function(){return(new this.constructor).copy(this)}},THREE.InstancedInterleavedBuffer=function(array,stride,meshPerAttribute){THREE.InterleavedBuffer.call(this,array,stride),this.meshPerAttribute=meshPerAttribute||1},THREE.InstancedInterleavedBuffer.prototype=Object.create(THREE.InterleavedBuffer.prototype),THREE.InstancedInterleavedBuffer.prototype.constructor=THREE.InstancedInterleavedBuffer,THREE.InstancedInterleavedBuffer.prototype.copy=function(source){return THREE.InterleavedBuffer.prototype.copy.call(this,source),this.meshPerAttribute=source.meshPerAttribute,this},THREE.InterleavedBufferAttribute=function(interleavedBuffer,itemSize,offset){this.uuid=THREE.Math.generateUUID(),this.data=interleavedBuffer,this.itemSize=itemSize,this.offset=offset},THREE.InterleavedBufferAttribute.prototype={constructor:THREE.InterleavedBufferAttribute,get length(){return console.warn("THREE.BufferAttribute: .length has been deprecated. Please use .count."),this.array.length},get count(){return this.data.array.length/this.data.stride},setX:function(index,x){return this.data.array[index*this.data.stride+this.offset]=x,this},setY:function(index,y){return this.data.array[index*this.data.stride+this.offset+1]=y,this},setZ:function(index,z){return this.data.array[index*this.data.stride+this.offset+2]=z,this},setW:function(index,w){return this.data.array[index*this.data.stride+this.offset+3]=w,this},getX:function(index){return this.data.array[index*this.data.stride+this.offset]},getY:function(index){return this.data.array[index*this.data.stride+this.offset+1]},getZ:function(index){return this.data.array[index*this.data.stride+this.offset+2]},getW:function(index){return this.data.array[index*this.data.stride+this.offset+3]},setXY:function(index,x,y){return index=index*this.data.stride+this.offset,this.data.array[index+0]=x,this.data.array[index+1]=y,this},setXYZ:function(index,x,y,z){return index=index*this.data.stride+this.offset,this.data.array[index+0]=x,this.data.array[index+1]=y,this.data.array[index+2]=z,this},setXYZW:function(index,x,y,z,w){return index=index*this.data.stride+this.offset,this.data.array[index+0]=x,this.data.array[index+1]=y,this.data.array[index+2]=z,this.data.array[index+3]=w,this}},THREE.Geometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++}),this.uuid=THREE.Math.generateUUID(),this.name="",this.type="Geometry",this.vertices=[],this.colors=[],this.faces=[],this.faceVertexUvs=[[]],this.morphTargets=[],this.morphNormals=[],this.skinWeights=[],this.skinIndices=[],this.lineDistances=[],this.boundingBox=null,this.boundingSphere=null,this.verticesNeedUpdate=!1,this.elementsNeedUpdate=!1,this.uvsNeedUpdate=!1,this.normalsNeedUpdate=!1,this.colorsNeedUpdate=!1,this.lineDistancesNeedUpdate=!1,this.groupsNeedUpdate=!1},THREE.Geometry.prototype={constructor:THREE.Geometry,applyMatrix:function(matrix){for(var normalMatrix=(new THREE.Matrix3).getNormalMatrix(matrix),i=0,il=this.vertices.length;il>i;i++){var vertex=this.vertices[i];vertex.applyMatrix4(matrix)}for(var i=0,il=this.faces.length;il>i;i++){var face=this.faces[i];face.normal.applyMatrix3(normalMatrix).normalize();for(var j=0,jl=face.vertexNormals.length;jl>j;j++)face.vertexNormals[j].applyMatrix3(normalMatrix).normalize();
+}null!==this.boundingBox&&this.computeBoundingBox(),null!==this.boundingSphere&&this.computeBoundingSphere(),this.verticesNeedUpdate=!0,this.normalsNeedUpdate=!0},rotateX:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationX(angle),this.applyMatrix(m1),this}}(),rotateY:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationY(angle),this.applyMatrix(m1),this}}(),rotateZ:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationZ(angle),this.applyMatrix(m1),this}}(),translate:function(){var m1;return function(x,y,z){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeTranslation(x,y,z),this.applyMatrix(m1),this}}(),scale:function(){var m1;return function(x,y,z){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeScale(x,y,z),this.applyMatrix(m1),this}}(),lookAt:function(){var obj;return function(vector){void 0===obj&&(obj=new THREE.Object3D),obj.lookAt(vector),obj.updateMatrix(),this.applyMatrix(obj.matrix)}}(),fromBufferGeometry:function(geometry){function addFace(a,b,c){var vertexNormals=void 0!==normals?[tempNormals[a].clone(),tempNormals[b].clone(),tempNormals[c].clone()]:[],vertexColors=void 0!==colors?[scope.colors[a].clone(),scope.colors[b].clone(),scope.colors[c].clone()]:[],face=new THREE.Face3(a,b,c,vertexNormals,vertexColors);scope.faces.push(face),void 0!==uvs&&scope.faceVertexUvs[0].push([tempUVs[a].clone(),tempUVs[b].clone(),tempUVs[c].clone()]),void 0!==uvs2&&scope.faceVertexUvs[1].push([tempUVs2[a].clone(),tempUVs2[b].clone(),tempUVs2[c].clone()])}var scope=this,indices=null!==geometry.index?geometry.index.array:void 0,attributes=geometry.attributes,vertices=attributes.position.array,normals=void 0!==attributes.normal?attributes.normal.array:void 0,colors=void 0!==attributes.color?attributes.color.array:void 0,uvs=void 0!==attributes.uv?attributes.uv.array:void 0,uvs2=void 0!==attributes.uv2?attributes.uv2.array:void 0;void 0!==uvs2&&(this.faceVertexUvs[1]=[]);for(var tempNormals=[],tempUVs=[],tempUVs2=[],i=0,j=0,k=0;i